162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	Generic address resolution entity
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *	Authors:
662306a36Sopenharmony_ci *	Pedro Roque		<roque@di.fc.ul.pt>
762306a36Sopenharmony_ci *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *	Fixes:
1062306a36Sopenharmony_ci *	Vitaly E. Lavrov	releasing NULL neighbor in neigh_add.
1162306a36Sopenharmony_ci *	Harald Welte		Add neighbour cache statistics like rtstat
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/kmemleak.h>
1862306a36Sopenharmony_ci#include <linux/types.h>
1962306a36Sopenharmony_ci#include <linux/kernel.h>
2062306a36Sopenharmony_ci#include <linux/module.h>
2162306a36Sopenharmony_ci#include <linux/socket.h>
2262306a36Sopenharmony_ci#include <linux/netdevice.h>
2362306a36Sopenharmony_ci#include <linux/proc_fs.h>
2462306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
2562306a36Sopenharmony_ci#include <linux/sysctl.h>
2662306a36Sopenharmony_ci#endif
2762306a36Sopenharmony_ci#include <linux/times.h>
2862306a36Sopenharmony_ci#include <net/net_namespace.h>
2962306a36Sopenharmony_ci#include <net/neighbour.h>
3062306a36Sopenharmony_ci#include <net/arp.h>
3162306a36Sopenharmony_ci#include <net/dst.h>
3262306a36Sopenharmony_ci#include <net/sock.h>
3362306a36Sopenharmony_ci#include <net/netevent.h>
3462306a36Sopenharmony_ci#include <net/netlink.h>
3562306a36Sopenharmony_ci#include <linux/rtnetlink.h>
3662306a36Sopenharmony_ci#include <linux/random.h>
3762306a36Sopenharmony_ci#include <linux/string.h>
3862306a36Sopenharmony_ci#include <linux/log2.h>
3962306a36Sopenharmony_ci#include <linux/inetdevice.h>
4062306a36Sopenharmony_ci#include <net/addrconf.h>
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#include <trace/events/neigh.h>
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define NEIGH_DEBUG 1
4562306a36Sopenharmony_ci#define neigh_dbg(level, fmt, ...)		\
4662306a36Sopenharmony_cido {						\
4762306a36Sopenharmony_ci	if (level <= NEIGH_DEBUG)		\
4862306a36Sopenharmony_ci		pr_debug(fmt, ##__VA_ARGS__);	\
4962306a36Sopenharmony_ci} while (0)
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define PNEIGH_HASHMASK		0xF
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic void neigh_timer_handler(struct timer_list *t);
5462306a36Sopenharmony_cistatic void __neigh_notify(struct neighbour *n, int type, int flags,
5562306a36Sopenharmony_ci			   u32 pid);
5662306a36Sopenharmony_cistatic void neigh_update_notify(struct neighbour *neigh, u32 nlmsg_pid);
5762306a36Sopenharmony_cistatic int pneigh_ifdown_and_unlock(struct neigh_table *tbl,
5862306a36Sopenharmony_ci				    struct net_device *dev);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
6162306a36Sopenharmony_cistatic const struct seq_operations neigh_stat_seq_ops;
6262306a36Sopenharmony_ci#endif
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/*
6562306a36Sopenharmony_ci   Neighbour hash table buckets are protected with rwlock tbl->lock.
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci   - All the scans/updates to hash buckets MUST be made under this lock.
6862306a36Sopenharmony_ci   - NOTHING clever should be made under this lock: no callbacks
6962306a36Sopenharmony_ci     to protocol backends, no attempts to send something to network.
7062306a36Sopenharmony_ci     It will result in deadlocks, if backend/driver wants to use neighbour
7162306a36Sopenharmony_ci     cache.
7262306a36Sopenharmony_ci   - If the entry requires some non-trivial actions, increase
7362306a36Sopenharmony_ci     its reference count and release table lock.
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci   Neighbour entries are protected:
7662306a36Sopenharmony_ci   - with reference count.
7762306a36Sopenharmony_ci   - with rwlock neigh->lock
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci   Reference count prevents destruction.
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci   neigh->lock mainly serializes ll address data and its validity state.
8262306a36Sopenharmony_ci   However, the same lock is used to protect another entry fields:
8362306a36Sopenharmony_ci    - timer
8462306a36Sopenharmony_ci    - resolution queue
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci   Again, nothing clever shall be made under neigh->lock,
8762306a36Sopenharmony_ci   the most complicated procedure, which we allow is dev->hard_header.
8862306a36Sopenharmony_ci   It is supposed, that dev->hard_header is simplistic and does
8962306a36Sopenharmony_ci   not make callbacks to neighbour tables.
9062306a36Sopenharmony_ci */
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic int neigh_blackhole(struct neighbour *neigh, struct sk_buff *skb)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	kfree_skb(skb);
9562306a36Sopenharmony_ci	return -ENETDOWN;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic void neigh_cleanup_and_release(struct neighbour *neigh)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	trace_neigh_cleanup_and_release(neigh, 0);
10162306a36Sopenharmony_ci	__neigh_notify(neigh, RTM_DELNEIGH, 0, 0);
10262306a36Sopenharmony_ci	call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh);
10362306a36Sopenharmony_ci	neigh_release(neigh);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/*
10762306a36Sopenharmony_ci * It is random distribution in the interval (1/2)*base...(3/2)*base.
10862306a36Sopenharmony_ci * It corresponds to default IPv6 settings and is not overridable,
10962306a36Sopenharmony_ci * because it is really reasonable choice.
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ciunsigned long neigh_rand_reach_time(unsigned long base)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	return base ? get_random_u32_below(base) + (base >> 1) : 0;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_rand_reach_time);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic void neigh_mark_dead(struct neighbour *n)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	n->dead = 1;
12162306a36Sopenharmony_ci	if (!list_empty(&n->gc_list)) {
12262306a36Sopenharmony_ci		list_del_init(&n->gc_list);
12362306a36Sopenharmony_ci		atomic_dec(&n->tbl->gc_entries);
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci	if (!list_empty(&n->managed_list))
12662306a36Sopenharmony_ci		list_del_init(&n->managed_list);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic void neigh_update_gc_list(struct neighbour *n)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	bool on_gc_list, exempt_from_gc;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	write_lock_bh(&n->tbl->lock);
13462306a36Sopenharmony_ci	write_lock(&n->lock);
13562306a36Sopenharmony_ci	if (n->dead)
13662306a36Sopenharmony_ci		goto out;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* remove from the gc list if new state is permanent or if neighbor
13962306a36Sopenharmony_ci	 * is externally learned; otherwise entry should be on the gc list
14062306a36Sopenharmony_ci	 */
14162306a36Sopenharmony_ci	exempt_from_gc = n->nud_state & NUD_PERMANENT ||
14262306a36Sopenharmony_ci			 n->flags & NTF_EXT_LEARNED;
14362306a36Sopenharmony_ci	on_gc_list = !list_empty(&n->gc_list);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (exempt_from_gc && on_gc_list) {
14662306a36Sopenharmony_ci		list_del_init(&n->gc_list);
14762306a36Sopenharmony_ci		atomic_dec(&n->tbl->gc_entries);
14862306a36Sopenharmony_ci	} else if (!exempt_from_gc && !on_gc_list) {
14962306a36Sopenharmony_ci		/* add entries to the tail; cleaning removes from the front */
15062306a36Sopenharmony_ci		list_add_tail(&n->gc_list, &n->tbl->gc_list);
15162306a36Sopenharmony_ci		atomic_inc(&n->tbl->gc_entries);
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ciout:
15462306a36Sopenharmony_ci	write_unlock(&n->lock);
15562306a36Sopenharmony_ci	write_unlock_bh(&n->tbl->lock);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic void neigh_update_managed_list(struct neighbour *n)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	bool on_managed_list, add_to_managed;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	write_lock_bh(&n->tbl->lock);
16362306a36Sopenharmony_ci	write_lock(&n->lock);
16462306a36Sopenharmony_ci	if (n->dead)
16562306a36Sopenharmony_ci		goto out;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	add_to_managed = n->flags & NTF_MANAGED;
16862306a36Sopenharmony_ci	on_managed_list = !list_empty(&n->managed_list);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (!add_to_managed && on_managed_list)
17162306a36Sopenharmony_ci		list_del_init(&n->managed_list);
17262306a36Sopenharmony_ci	else if (add_to_managed && !on_managed_list)
17362306a36Sopenharmony_ci		list_add_tail(&n->managed_list, &n->tbl->managed_list);
17462306a36Sopenharmony_ciout:
17562306a36Sopenharmony_ci	write_unlock(&n->lock);
17662306a36Sopenharmony_ci	write_unlock_bh(&n->tbl->lock);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic void neigh_update_flags(struct neighbour *neigh, u32 flags, int *notify,
18062306a36Sopenharmony_ci			       bool *gc_update, bool *managed_update)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	u32 ndm_flags, old_flags = neigh->flags;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (!(flags & NEIGH_UPDATE_F_ADMIN))
18562306a36Sopenharmony_ci		return;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	ndm_flags  = (flags & NEIGH_UPDATE_F_EXT_LEARNED) ? NTF_EXT_LEARNED : 0;
18862306a36Sopenharmony_ci	ndm_flags |= (flags & NEIGH_UPDATE_F_MANAGED) ? NTF_MANAGED : 0;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if ((old_flags ^ ndm_flags) & NTF_EXT_LEARNED) {
19162306a36Sopenharmony_ci		if (ndm_flags & NTF_EXT_LEARNED)
19262306a36Sopenharmony_ci			neigh->flags |= NTF_EXT_LEARNED;
19362306a36Sopenharmony_ci		else
19462306a36Sopenharmony_ci			neigh->flags &= ~NTF_EXT_LEARNED;
19562306a36Sopenharmony_ci		*notify = 1;
19662306a36Sopenharmony_ci		*gc_update = true;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci	if ((old_flags ^ ndm_flags) & NTF_MANAGED) {
19962306a36Sopenharmony_ci		if (ndm_flags & NTF_MANAGED)
20062306a36Sopenharmony_ci			neigh->flags |= NTF_MANAGED;
20162306a36Sopenharmony_ci		else
20262306a36Sopenharmony_ci			neigh->flags &= ~NTF_MANAGED;
20362306a36Sopenharmony_ci		*notify = 1;
20462306a36Sopenharmony_ci		*managed_update = true;
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic bool neigh_del(struct neighbour *n, struct neighbour __rcu **np,
20962306a36Sopenharmony_ci		      struct neigh_table *tbl)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	bool retval = false;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	write_lock(&n->lock);
21462306a36Sopenharmony_ci	if (refcount_read(&n->refcnt) == 1) {
21562306a36Sopenharmony_ci		struct neighbour *neigh;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		neigh = rcu_dereference_protected(n->next,
21862306a36Sopenharmony_ci						  lockdep_is_held(&tbl->lock));
21962306a36Sopenharmony_ci		rcu_assign_pointer(*np, neigh);
22062306a36Sopenharmony_ci		neigh_mark_dead(n);
22162306a36Sopenharmony_ci		retval = true;
22262306a36Sopenharmony_ci	}
22362306a36Sopenharmony_ci	write_unlock(&n->lock);
22462306a36Sopenharmony_ci	if (retval)
22562306a36Sopenharmony_ci		neigh_cleanup_and_release(n);
22662306a36Sopenharmony_ci	return retval;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cibool neigh_remove_one(struct neighbour *ndel, struct neigh_table *tbl)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct neigh_hash_table *nht;
23262306a36Sopenharmony_ci	void *pkey = ndel->primary_key;
23362306a36Sopenharmony_ci	u32 hash_val;
23462306a36Sopenharmony_ci	struct neighbour *n;
23562306a36Sopenharmony_ci	struct neighbour __rcu **np;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	nht = rcu_dereference_protected(tbl->nht,
23862306a36Sopenharmony_ci					lockdep_is_held(&tbl->lock));
23962306a36Sopenharmony_ci	hash_val = tbl->hash(pkey, ndel->dev, nht->hash_rnd);
24062306a36Sopenharmony_ci	hash_val = hash_val >> (32 - nht->hash_shift);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	np = &nht->hash_buckets[hash_val];
24362306a36Sopenharmony_ci	while ((n = rcu_dereference_protected(*np,
24462306a36Sopenharmony_ci					      lockdep_is_held(&tbl->lock)))) {
24562306a36Sopenharmony_ci		if (n == ndel)
24662306a36Sopenharmony_ci			return neigh_del(n, np, tbl);
24762306a36Sopenharmony_ci		np = &n->next;
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci	return false;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic int neigh_forced_gc(struct neigh_table *tbl)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	int max_clean = atomic_read(&tbl->gc_entries) -
25562306a36Sopenharmony_ci			READ_ONCE(tbl->gc_thresh2);
25662306a36Sopenharmony_ci	u64 tmax = ktime_get_ns() + NSEC_PER_MSEC;
25762306a36Sopenharmony_ci	unsigned long tref = jiffies - 5 * HZ;
25862306a36Sopenharmony_ci	struct neighbour *n, *tmp;
25962306a36Sopenharmony_ci	int shrunk = 0;
26062306a36Sopenharmony_ci	int loop = 0;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	NEIGH_CACHE_STAT_INC(tbl, forced_gc_runs);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	write_lock_bh(&tbl->lock);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	list_for_each_entry_safe(n, tmp, &tbl->gc_list, gc_list) {
26762306a36Sopenharmony_ci		if (refcount_read(&n->refcnt) == 1) {
26862306a36Sopenharmony_ci			bool remove = false;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci			write_lock(&n->lock);
27162306a36Sopenharmony_ci			if ((n->nud_state == NUD_FAILED) ||
27262306a36Sopenharmony_ci			    (n->nud_state == NUD_NOARP) ||
27362306a36Sopenharmony_ci			    (tbl->is_multicast &&
27462306a36Sopenharmony_ci			     tbl->is_multicast(n->primary_key)) ||
27562306a36Sopenharmony_ci			    !time_in_range(n->updated, tref, jiffies))
27662306a36Sopenharmony_ci				remove = true;
27762306a36Sopenharmony_ci			write_unlock(&n->lock);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci			if (remove && neigh_remove_one(n, tbl))
28062306a36Sopenharmony_ci				shrunk++;
28162306a36Sopenharmony_ci			if (shrunk >= max_clean)
28262306a36Sopenharmony_ci				break;
28362306a36Sopenharmony_ci			if (++loop == 16) {
28462306a36Sopenharmony_ci				if (ktime_get_ns() > tmax)
28562306a36Sopenharmony_ci					goto unlock;
28662306a36Sopenharmony_ci				loop = 0;
28762306a36Sopenharmony_ci			}
28862306a36Sopenharmony_ci		}
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	WRITE_ONCE(tbl->last_flush, jiffies);
29262306a36Sopenharmony_ciunlock:
29362306a36Sopenharmony_ci	write_unlock_bh(&tbl->lock);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return shrunk;
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic void neigh_add_timer(struct neighbour *n, unsigned long when)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	/* Use safe distance from the jiffies - LONG_MAX point while timer
30162306a36Sopenharmony_ci	 * is running in DELAY/PROBE state but still show to user space
30262306a36Sopenharmony_ci	 * large times in the past.
30362306a36Sopenharmony_ci	 */
30462306a36Sopenharmony_ci	unsigned long mint = jiffies - (LONG_MAX - 86400 * HZ);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	neigh_hold(n);
30762306a36Sopenharmony_ci	if (!time_in_range(n->confirmed, mint, jiffies))
30862306a36Sopenharmony_ci		n->confirmed = mint;
30962306a36Sopenharmony_ci	if (time_before(n->used, n->confirmed))
31062306a36Sopenharmony_ci		n->used = n->confirmed;
31162306a36Sopenharmony_ci	if (unlikely(mod_timer(&n->timer, when))) {
31262306a36Sopenharmony_ci		printk("NEIGH: BUG, double timer add, state is %x\n",
31362306a36Sopenharmony_ci		       n->nud_state);
31462306a36Sopenharmony_ci		dump_stack();
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic int neigh_del_timer(struct neighbour *n)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	if ((n->nud_state & NUD_IN_TIMER) &&
32162306a36Sopenharmony_ci	    del_timer(&n->timer)) {
32262306a36Sopenharmony_ci		neigh_release(n);
32362306a36Sopenharmony_ci		return 1;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci	return 0;
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic struct neigh_parms *neigh_get_dev_parms_rcu(struct net_device *dev,
32962306a36Sopenharmony_ci						   int family)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	switch (family) {
33262306a36Sopenharmony_ci	case AF_INET:
33362306a36Sopenharmony_ci		return __in_dev_arp_parms_get_rcu(dev);
33462306a36Sopenharmony_ci	case AF_INET6:
33562306a36Sopenharmony_ci		return __in6_dev_nd_parms_get_rcu(dev);
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci	return NULL;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic void neigh_parms_qlen_dec(struct net_device *dev, int family)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	struct neigh_parms *p;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	rcu_read_lock();
34562306a36Sopenharmony_ci	p = neigh_get_dev_parms_rcu(dev, family);
34662306a36Sopenharmony_ci	if (p)
34762306a36Sopenharmony_ci		p->qlen--;
34862306a36Sopenharmony_ci	rcu_read_unlock();
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic void pneigh_queue_purge(struct sk_buff_head *list, struct net *net,
35262306a36Sopenharmony_ci			       int family)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct sk_buff_head tmp;
35562306a36Sopenharmony_ci	unsigned long flags;
35662306a36Sopenharmony_ci	struct sk_buff *skb;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	skb_queue_head_init(&tmp);
35962306a36Sopenharmony_ci	spin_lock_irqsave(&list->lock, flags);
36062306a36Sopenharmony_ci	skb = skb_peek(list);
36162306a36Sopenharmony_ci	while (skb != NULL) {
36262306a36Sopenharmony_ci		struct sk_buff *skb_next = skb_peek_next(skb, list);
36362306a36Sopenharmony_ci		struct net_device *dev = skb->dev;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		if (net == NULL || net_eq(dev_net(dev), net)) {
36662306a36Sopenharmony_ci			neigh_parms_qlen_dec(dev, family);
36762306a36Sopenharmony_ci			__skb_unlink(skb, list);
36862306a36Sopenharmony_ci			__skb_queue_tail(&tmp, skb);
36962306a36Sopenharmony_ci		}
37062306a36Sopenharmony_ci		skb = skb_next;
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci	spin_unlock_irqrestore(&list->lock, flags);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	while ((skb = __skb_dequeue(&tmp))) {
37562306a36Sopenharmony_ci		dev_put(skb->dev);
37662306a36Sopenharmony_ci		kfree_skb(skb);
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev,
38162306a36Sopenharmony_ci			    bool skip_perm)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	int i;
38462306a36Sopenharmony_ci	struct neigh_hash_table *nht;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	nht = rcu_dereference_protected(tbl->nht,
38762306a36Sopenharmony_ci					lockdep_is_held(&tbl->lock));
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	for (i = 0; i < (1 << nht->hash_shift); i++) {
39062306a36Sopenharmony_ci		struct neighbour *n;
39162306a36Sopenharmony_ci		struct neighbour __rcu **np = &nht->hash_buckets[i];
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		while ((n = rcu_dereference_protected(*np,
39462306a36Sopenharmony_ci					lockdep_is_held(&tbl->lock))) != NULL) {
39562306a36Sopenharmony_ci			if (dev && n->dev != dev) {
39662306a36Sopenharmony_ci				np = &n->next;
39762306a36Sopenharmony_ci				continue;
39862306a36Sopenharmony_ci			}
39962306a36Sopenharmony_ci			if (skip_perm && n->nud_state & NUD_PERMANENT) {
40062306a36Sopenharmony_ci				np = &n->next;
40162306a36Sopenharmony_ci				continue;
40262306a36Sopenharmony_ci			}
40362306a36Sopenharmony_ci			rcu_assign_pointer(*np,
40462306a36Sopenharmony_ci				   rcu_dereference_protected(n->next,
40562306a36Sopenharmony_ci						lockdep_is_held(&tbl->lock)));
40662306a36Sopenharmony_ci			write_lock(&n->lock);
40762306a36Sopenharmony_ci			neigh_del_timer(n);
40862306a36Sopenharmony_ci			neigh_mark_dead(n);
40962306a36Sopenharmony_ci			if (refcount_read(&n->refcnt) != 1) {
41062306a36Sopenharmony_ci				/* The most unpleasant situation.
41162306a36Sopenharmony_ci				   We must destroy neighbour entry,
41262306a36Sopenharmony_ci				   but someone still uses it.
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci				   The destroy will be delayed until
41562306a36Sopenharmony_ci				   the last user releases us, but
41662306a36Sopenharmony_ci				   we must kill timers etc. and move
41762306a36Sopenharmony_ci				   it to safe state.
41862306a36Sopenharmony_ci				 */
41962306a36Sopenharmony_ci				__skb_queue_purge(&n->arp_queue);
42062306a36Sopenharmony_ci				n->arp_queue_len_bytes = 0;
42162306a36Sopenharmony_ci				WRITE_ONCE(n->output, neigh_blackhole);
42262306a36Sopenharmony_ci				if (n->nud_state & NUD_VALID)
42362306a36Sopenharmony_ci					n->nud_state = NUD_NOARP;
42462306a36Sopenharmony_ci				else
42562306a36Sopenharmony_ci					n->nud_state = NUD_NONE;
42662306a36Sopenharmony_ci				neigh_dbg(2, "neigh %p is stray\n", n);
42762306a36Sopenharmony_ci			}
42862306a36Sopenharmony_ci			write_unlock(&n->lock);
42962306a36Sopenharmony_ci			neigh_cleanup_and_release(n);
43062306a36Sopenharmony_ci		}
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_civoid neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	write_lock_bh(&tbl->lock);
43762306a36Sopenharmony_ci	neigh_flush_dev(tbl, dev, false);
43862306a36Sopenharmony_ci	write_unlock_bh(&tbl->lock);
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_changeaddr);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic int __neigh_ifdown(struct neigh_table *tbl, struct net_device *dev,
44362306a36Sopenharmony_ci			  bool skip_perm)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	write_lock_bh(&tbl->lock);
44662306a36Sopenharmony_ci	neigh_flush_dev(tbl, dev, skip_perm);
44762306a36Sopenharmony_ci	pneigh_ifdown_and_unlock(tbl, dev);
44862306a36Sopenharmony_ci	pneigh_queue_purge(&tbl->proxy_queue, dev ? dev_net(dev) : NULL,
44962306a36Sopenharmony_ci			   tbl->family);
45062306a36Sopenharmony_ci	if (skb_queue_empty_lockless(&tbl->proxy_queue))
45162306a36Sopenharmony_ci		del_timer_sync(&tbl->proxy_timer);
45262306a36Sopenharmony_ci	return 0;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ciint neigh_carrier_down(struct neigh_table *tbl, struct net_device *dev)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	__neigh_ifdown(tbl, dev, true);
45862306a36Sopenharmony_ci	return 0;
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_carrier_down);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ciint neigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	__neigh_ifdown(tbl, dev, false);
46562306a36Sopenharmony_ci	return 0;
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_ifdown);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic struct neighbour *neigh_alloc(struct neigh_table *tbl,
47062306a36Sopenharmony_ci				     struct net_device *dev,
47162306a36Sopenharmony_ci				     u32 flags, bool exempt_from_gc)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	struct neighbour *n = NULL;
47462306a36Sopenharmony_ci	unsigned long now = jiffies;
47562306a36Sopenharmony_ci	int entries, gc_thresh3;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	if (exempt_from_gc)
47862306a36Sopenharmony_ci		goto do_alloc;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	entries = atomic_inc_return(&tbl->gc_entries) - 1;
48162306a36Sopenharmony_ci	gc_thresh3 = READ_ONCE(tbl->gc_thresh3);
48262306a36Sopenharmony_ci	if (entries >= gc_thresh3 ||
48362306a36Sopenharmony_ci	    (entries >= READ_ONCE(tbl->gc_thresh2) &&
48462306a36Sopenharmony_ci	     time_after(now, READ_ONCE(tbl->last_flush) + 5 * HZ))) {
48562306a36Sopenharmony_ci		if (!neigh_forced_gc(tbl) && entries >= gc_thresh3) {
48662306a36Sopenharmony_ci			net_info_ratelimited("%s: neighbor table overflow!\n",
48762306a36Sopenharmony_ci					     tbl->id);
48862306a36Sopenharmony_ci			NEIGH_CACHE_STAT_INC(tbl, table_fulls);
48962306a36Sopenharmony_ci			goto out_entries;
49062306a36Sopenharmony_ci		}
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cido_alloc:
49462306a36Sopenharmony_ci	n = kzalloc(tbl->entry_size + dev->neigh_priv_len, GFP_ATOMIC);
49562306a36Sopenharmony_ci	if (!n)
49662306a36Sopenharmony_ci		goto out_entries;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	__skb_queue_head_init(&n->arp_queue);
49962306a36Sopenharmony_ci	rwlock_init(&n->lock);
50062306a36Sopenharmony_ci	seqlock_init(&n->ha_lock);
50162306a36Sopenharmony_ci	n->updated	  = n->used = now;
50262306a36Sopenharmony_ci	n->nud_state	  = NUD_NONE;
50362306a36Sopenharmony_ci	n->output	  = neigh_blackhole;
50462306a36Sopenharmony_ci	n->flags	  = flags;
50562306a36Sopenharmony_ci	seqlock_init(&n->hh.hh_lock);
50662306a36Sopenharmony_ci	n->parms	  = neigh_parms_clone(&tbl->parms);
50762306a36Sopenharmony_ci	timer_setup(&n->timer, neigh_timer_handler, 0);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	NEIGH_CACHE_STAT_INC(tbl, allocs);
51062306a36Sopenharmony_ci	n->tbl		  = tbl;
51162306a36Sopenharmony_ci	refcount_set(&n->refcnt, 1);
51262306a36Sopenharmony_ci	n->dead		  = 1;
51362306a36Sopenharmony_ci	INIT_LIST_HEAD(&n->gc_list);
51462306a36Sopenharmony_ci	INIT_LIST_HEAD(&n->managed_list);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	atomic_inc(&tbl->entries);
51762306a36Sopenharmony_ciout:
51862306a36Sopenharmony_ci	return n;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ciout_entries:
52162306a36Sopenharmony_ci	if (!exempt_from_gc)
52262306a36Sopenharmony_ci		atomic_dec(&tbl->gc_entries);
52362306a36Sopenharmony_ci	goto out;
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistatic void neigh_get_hash_rnd(u32 *x)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	*x = get_random_u32() | 1;
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_cistatic struct neigh_hash_table *neigh_hash_alloc(unsigned int shift)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	size_t size = (1 << shift) * sizeof(struct neighbour *);
53462306a36Sopenharmony_ci	struct neigh_hash_table *ret;
53562306a36Sopenharmony_ci	struct neighbour __rcu **buckets;
53662306a36Sopenharmony_ci	int i;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	ret = kmalloc(sizeof(*ret), GFP_ATOMIC);
53962306a36Sopenharmony_ci	if (!ret)
54062306a36Sopenharmony_ci		return NULL;
54162306a36Sopenharmony_ci	if (size <= PAGE_SIZE) {
54262306a36Sopenharmony_ci		buckets = kzalloc(size, GFP_ATOMIC);
54362306a36Sopenharmony_ci	} else {
54462306a36Sopenharmony_ci		buckets = (struct neighbour __rcu **)
54562306a36Sopenharmony_ci			  __get_free_pages(GFP_ATOMIC | __GFP_ZERO,
54662306a36Sopenharmony_ci					   get_order(size));
54762306a36Sopenharmony_ci		kmemleak_alloc(buckets, size, 1, GFP_ATOMIC);
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci	if (!buckets) {
55062306a36Sopenharmony_ci		kfree(ret);
55162306a36Sopenharmony_ci		return NULL;
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci	ret->hash_buckets = buckets;
55462306a36Sopenharmony_ci	ret->hash_shift = shift;
55562306a36Sopenharmony_ci	for (i = 0; i < NEIGH_NUM_HASH_RND; i++)
55662306a36Sopenharmony_ci		neigh_get_hash_rnd(&ret->hash_rnd[i]);
55762306a36Sopenharmony_ci	return ret;
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic void neigh_hash_free_rcu(struct rcu_head *head)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	struct neigh_hash_table *nht = container_of(head,
56362306a36Sopenharmony_ci						    struct neigh_hash_table,
56462306a36Sopenharmony_ci						    rcu);
56562306a36Sopenharmony_ci	size_t size = (1 << nht->hash_shift) * sizeof(struct neighbour *);
56662306a36Sopenharmony_ci	struct neighbour __rcu **buckets = nht->hash_buckets;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (size <= PAGE_SIZE) {
56962306a36Sopenharmony_ci		kfree(buckets);
57062306a36Sopenharmony_ci	} else {
57162306a36Sopenharmony_ci		kmemleak_free(buckets);
57262306a36Sopenharmony_ci		free_pages((unsigned long)buckets, get_order(size));
57362306a36Sopenharmony_ci	}
57462306a36Sopenharmony_ci	kfree(nht);
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic struct neigh_hash_table *neigh_hash_grow(struct neigh_table *tbl,
57862306a36Sopenharmony_ci						unsigned long new_shift)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	unsigned int i, hash;
58162306a36Sopenharmony_ci	struct neigh_hash_table *new_nht, *old_nht;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	NEIGH_CACHE_STAT_INC(tbl, hash_grows);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	old_nht = rcu_dereference_protected(tbl->nht,
58662306a36Sopenharmony_ci					    lockdep_is_held(&tbl->lock));
58762306a36Sopenharmony_ci	new_nht = neigh_hash_alloc(new_shift);
58862306a36Sopenharmony_ci	if (!new_nht)
58962306a36Sopenharmony_ci		return old_nht;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	for (i = 0; i < (1 << old_nht->hash_shift); i++) {
59262306a36Sopenharmony_ci		struct neighbour *n, *next;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci		for (n = rcu_dereference_protected(old_nht->hash_buckets[i],
59562306a36Sopenharmony_ci						   lockdep_is_held(&tbl->lock));
59662306a36Sopenharmony_ci		     n != NULL;
59762306a36Sopenharmony_ci		     n = next) {
59862306a36Sopenharmony_ci			hash = tbl->hash(n->primary_key, n->dev,
59962306a36Sopenharmony_ci					 new_nht->hash_rnd);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci			hash >>= (32 - new_nht->hash_shift);
60262306a36Sopenharmony_ci			next = rcu_dereference_protected(n->next,
60362306a36Sopenharmony_ci						lockdep_is_held(&tbl->lock));
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci			rcu_assign_pointer(n->next,
60662306a36Sopenharmony_ci					   rcu_dereference_protected(
60762306a36Sopenharmony_ci						new_nht->hash_buckets[hash],
60862306a36Sopenharmony_ci						lockdep_is_held(&tbl->lock)));
60962306a36Sopenharmony_ci			rcu_assign_pointer(new_nht->hash_buckets[hash], n);
61062306a36Sopenharmony_ci		}
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	rcu_assign_pointer(tbl->nht, new_nht);
61462306a36Sopenharmony_ci	call_rcu(&old_nht->rcu, neigh_hash_free_rcu);
61562306a36Sopenharmony_ci	return new_nht;
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_cistruct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,
61962306a36Sopenharmony_ci			       struct net_device *dev)
62062306a36Sopenharmony_ci{
62162306a36Sopenharmony_ci	struct neighbour *n;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	NEIGH_CACHE_STAT_INC(tbl, lookups);
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	rcu_read_lock();
62662306a36Sopenharmony_ci	n = __neigh_lookup_noref(tbl, pkey, dev);
62762306a36Sopenharmony_ci	if (n) {
62862306a36Sopenharmony_ci		if (!refcount_inc_not_zero(&n->refcnt))
62962306a36Sopenharmony_ci			n = NULL;
63062306a36Sopenharmony_ci		NEIGH_CACHE_STAT_INC(tbl, hits);
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	rcu_read_unlock();
63462306a36Sopenharmony_ci	return n;
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_lookup);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_cistatic struct neighbour *
63962306a36Sopenharmony_ci___neigh_create(struct neigh_table *tbl, const void *pkey,
64062306a36Sopenharmony_ci		struct net_device *dev, u32 flags,
64162306a36Sopenharmony_ci		bool exempt_from_gc, bool want_ref)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	u32 hash_val, key_len = tbl->key_len;
64462306a36Sopenharmony_ci	struct neighbour *n1, *rc, *n;
64562306a36Sopenharmony_ci	struct neigh_hash_table *nht;
64662306a36Sopenharmony_ci	int error;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	n = neigh_alloc(tbl, dev, flags, exempt_from_gc);
64962306a36Sopenharmony_ci	trace_neigh_create(tbl, dev, pkey, n, exempt_from_gc);
65062306a36Sopenharmony_ci	if (!n) {
65162306a36Sopenharmony_ci		rc = ERR_PTR(-ENOBUFS);
65262306a36Sopenharmony_ci		goto out;
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	memcpy(n->primary_key, pkey, key_len);
65662306a36Sopenharmony_ci	n->dev = dev;
65762306a36Sopenharmony_ci	netdev_hold(dev, &n->dev_tracker, GFP_ATOMIC);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	/* Protocol specific setup. */
66062306a36Sopenharmony_ci	if (tbl->constructor &&	(error = tbl->constructor(n)) < 0) {
66162306a36Sopenharmony_ci		rc = ERR_PTR(error);
66262306a36Sopenharmony_ci		goto out_neigh_release;
66362306a36Sopenharmony_ci	}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	if (dev->netdev_ops->ndo_neigh_construct) {
66662306a36Sopenharmony_ci		error = dev->netdev_ops->ndo_neigh_construct(dev, n);
66762306a36Sopenharmony_ci		if (error < 0) {
66862306a36Sopenharmony_ci			rc = ERR_PTR(error);
66962306a36Sopenharmony_ci			goto out_neigh_release;
67062306a36Sopenharmony_ci		}
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	/* Device specific setup. */
67462306a36Sopenharmony_ci	if (n->parms->neigh_setup &&
67562306a36Sopenharmony_ci	    (error = n->parms->neigh_setup(n)) < 0) {
67662306a36Sopenharmony_ci		rc = ERR_PTR(error);
67762306a36Sopenharmony_ci		goto out_neigh_release;
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	n->confirmed = jiffies - (NEIGH_VAR(n->parms, BASE_REACHABLE_TIME) << 1);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	write_lock_bh(&tbl->lock);
68362306a36Sopenharmony_ci	nht = rcu_dereference_protected(tbl->nht,
68462306a36Sopenharmony_ci					lockdep_is_held(&tbl->lock));
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	if (atomic_read(&tbl->entries) > (1 << nht->hash_shift))
68762306a36Sopenharmony_ci		nht = neigh_hash_grow(tbl, nht->hash_shift + 1);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	hash_val = tbl->hash(n->primary_key, dev, nht->hash_rnd) >> (32 - nht->hash_shift);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	if (n->parms->dead) {
69262306a36Sopenharmony_ci		rc = ERR_PTR(-EINVAL);
69362306a36Sopenharmony_ci		goto out_tbl_unlock;
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	for (n1 = rcu_dereference_protected(nht->hash_buckets[hash_val],
69762306a36Sopenharmony_ci					    lockdep_is_held(&tbl->lock));
69862306a36Sopenharmony_ci	     n1 != NULL;
69962306a36Sopenharmony_ci	     n1 = rcu_dereference_protected(n1->next,
70062306a36Sopenharmony_ci			lockdep_is_held(&tbl->lock))) {
70162306a36Sopenharmony_ci		if (dev == n1->dev && !memcmp(n1->primary_key, n->primary_key, key_len)) {
70262306a36Sopenharmony_ci			if (want_ref)
70362306a36Sopenharmony_ci				neigh_hold(n1);
70462306a36Sopenharmony_ci			rc = n1;
70562306a36Sopenharmony_ci			goto out_tbl_unlock;
70662306a36Sopenharmony_ci		}
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	n->dead = 0;
71062306a36Sopenharmony_ci	if (!exempt_from_gc)
71162306a36Sopenharmony_ci		list_add_tail(&n->gc_list, &n->tbl->gc_list);
71262306a36Sopenharmony_ci	if (n->flags & NTF_MANAGED)
71362306a36Sopenharmony_ci		list_add_tail(&n->managed_list, &n->tbl->managed_list);
71462306a36Sopenharmony_ci	if (want_ref)
71562306a36Sopenharmony_ci		neigh_hold(n);
71662306a36Sopenharmony_ci	rcu_assign_pointer(n->next,
71762306a36Sopenharmony_ci			   rcu_dereference_protected(nht->hash_buckets[hash_val],
71862306a36Sopenharmony_ci						     lockdep_is_held(&tbl->lock)));
71962306a36Sopenharmony_ci	rcu_assign_pointer(nht->hash_buckets[hash_val], n);
72062306a36Sopenharmony_ci	write_unlock_bh(&tbl->lock);
72162306a36Sopenharmony_ci	neigh_dbg(2, "neigh %p is created\n", n);
72262306a36Sopenharmony_ci	rc = n;
72362306a36Sopenharmony_ciout:
72462306a36Sopenharmony_ci	return rc;
72562306a36Sopenharmony_ciout_tbl_unlock:
72662306a36Sopenharmony_ci	write_unlock_bh(&tbl->lock);
72762306a36Sopenharmony_ciout_neigh_release:
72862306a36Sopenharmony_ci	if (!exempt_from_gc)
72962306a36Sopenharmony_ci		atomic_dec(&tbl->gc_entries);
73062306a36Sopenharmony_ci	neigh_release(n);
73162306a36Sopenharmony_ci	goto out;
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_cistruct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,
73562306a36Sopenharmony_ci				 struct net_device *dev, bool want_ref)
73662306a36Sopenharmony_ci{
73762306a36Sopenharmony_ci	return ___neigh_create(tbl, pkey, dev, 0, false, want_ref);
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ciEXPORT_SYMBOL(__neigh_create);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_cistatic u32 pneigh_hash(const void *pkey, unsigned int key_len)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	u32 hash_val = *(u32 *)(pkey + key_len - 4);
74462306a36Sopenharmony_ci	hash_val ^= (hash_val >> 16);
74562306a36Sopenharmony_ci	hash_val ^= hash_val >> 8;
74662306a36Sopenharmony_ci	hash_val ^= hash_val >> 4;
74762306a36Sopenharmony_ci	hash_val &= PNEIGH_HASHMASK;
74862306a36Sopenharmony_ci	return hash_val;
74962306a36Sopenharmony_ci}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_cistatic struct pneigh_entry *__pneigh_lookup_1(struct pneigh_entry *n,
75262306a36Sopenharmony_ci					      struct net *net,
75362306a36Sopenharmony_ci					      const void *pkey,
75462306a36Sopenharmony_ci					      unsigned int key_len,
75562306a36Sopenharmony_ci					      struct net_device *dev)
75662306a36Sopenharmony_ci{
75762306a36Sopenharmony_ci	while (n) {
75862306a36Sopenharmony_ci		if (!memcmp(n->key, pkey, key_len) &&
75962306a36Sopenharmony_ci		    net_eq(pneigh_net(n), net) &&
76062306a36Sopenharmony_ci		    (n->dev == dev || !n->dev))
76162306a36Sopenharmony_ci			return n;
76262306a36Sopenharmony_ci		n = n->next;
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci	return NULL;
76562306a36Sopenharmony_ci}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cistruct pneigh_entry *__pneigh_lookup(struct neigh_table *tbl,
76862306a36Sopenharmony_ci		struct net *net, const void *pkey, struct net_device *dev)
76962306a36Sopenharmony_ci{
77062306a36Sopenharmony_ci	unsigned int key_len = tbl->key_len;
77162306a36Sopenharmony_ci	u32 hash_val = pneigh_hash(pkey, key_len);
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	return __pneigh_lookup_1(tbl->phash_buckets[hash_val],
77462306a36Sopenharmony_ci				 net, pkey, key_len, dev);
77562306a36Sopenharmony_ci}
77662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__pneigh_lookup);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_cistruct pneigh_entry * pneigh_lookup(struct neigh_table *tbl,
77962306a36Sopenharmony_ci				    struct net *net, const void *pkey,
78062306a36Sopenharmony_ci				    struct net_device *dev, int creat)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	struct pneigh_entry *n;
78362306a36Sopenharmony_ci	unsigned int key_len = tbl->key_len;
78462306a36Sopenharmony_ci	u32 hash_val = pneigh_hash(pkey, key_len);
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	read_lock_bh(&tbl->lock);
78762306a36Sopenharmony_ci	n = __pneigh_lookup_1(tbl->phash_buckets[hash_val],
78862306a36Sopenharmony_ci			      net, pkey, key_len, dev);
78962306a36Sopenharmony_ci	read_unlock_bh(&tbl->lock);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	if (n || !creat)
79262306a36Sopenharmony_ci		goto out;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	ASSERT_RTNL();
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	n = kzalloc(sizeof(*n) + key_len, GFP_KERNEL);
79762306a36Sopenharmony_ci	if (!n)
79862306a36Sopenharmony_ci		goto out;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	write_pnet(&n->net, net);
80162306a36Sopenharmony_ci	memcpy(n->key, pkey, key_len);
80262306a36Sopenharmony_ci	n->dev = dev;
80362306a36Sopenharmony_ci	netdev_hold(dev, &n->dev_tracker, GFP_KERNEL);
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	if (tbl->pconstructor && tbl->pconstructor(n)) {
80662306a36Sopenharmony_ci		netdev_put(dev, &n->dev_tracker);
80762306a36Sopenharmony_ci		kfree(n);
80862306a36Sopenharmony_ci		n = NULL;
80962306a36Sopenharmony_ci		goto out;
81062306a36Sopenharmony_ci	}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	write_lock_bh(&tbl->lock);
81362306a36Sopenharmony_ci	n->next = tbl->phash_buckets[hash_val];
81462306a36Sopenharmony_ci	tbl->phash_buckets[hash_val] = n;
81562306a36Sopenharmony_ci	write_unlock_bh(&tbl->lock);
81662306a36Sopenharmony_ciout:
81762306a36Sopenharmony_ci	return n;
81862306a36Sopenharmony_ci}
81962306a36Sopenharmony_ciEXPORT_SYMBOL(pneigh_lookup);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ciint pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey,
82362306a36Sopenharmony_ci		  struct net_device *dev)
82462306a36Sopenharmony_ci{
82562306a36Sopenharmony_ci	struct pneigh_entry *n, **np;
82662306a36Sopenharmony_ci	unsigned int key_len = tbl->key_len;
82762306a36Sopenharmony_ci	u32 hash_val = pneigh_hash(pkey, key_len);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	write_lock_bh(&tbl->lock);
83062306a36Sopenharmony_ci	for (np = &tbl->phash_buckets[hash_val]; (n = *np) != NULL;
83162306a36Sopenharmony_ci	     np = &n->next) {
83262306a36Sopenharmony_ci		if (!memcmp(n->key, pkey, key_len) && n->dev == dev &&
83362306a36Sopenharmony_ci		    net_eq(pneigh_net(n), net)) {
83462306a36Sopenharmony_ci			*np = n->next;
83562306a36Sopenharmony_ci			write_unlock_bh(&tbl->lock);
83662306a36Sopenharmony_ci			if (tbl->pdestructor)
83762306a36Sopenharmony_ci				tbl->pdestructor(n);
83862306a36Sopenharmony_ci			netdev_put(n->dev, &n->dev_tracker);
83962306a36Sopenharmony_ci			kfree(n);
84062306a36Sopenharmony_ci			return 0;
84162306a36Sopenharmony_ci		}
84262306a36Sopenharmony_ci	}
84362306a36Sopenharmony_ci	write_unlock_bh(&tbl->lock);
84462306a36Sopenharmony_ci	return -ENOENT;
84562306a36Sopenharmony_ci}
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_cistatic int pneigh_ifdown_and_unlock(struct neigh_table *tbl,
84862306a36Sopenharmony_ci				    struct net_device *dev)
84962306a36Sopenharmony_ci{
85062306a36Sopenharmony_ci	struct pneigh_entry *n, **np, *freelist = NULL;
85162306a36Sopenharmony_ci	u32 h;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	for (h = 0; h <= PNEIGH_HASHMASK; h++) {
85462306a36Sopenharmony_ci		np = &tbl->phash_buckets[h];
85562306a36Sopenharmony_ci		while ((n = *np) != NULL) {
85662306a36Sopenharmony_ci			if (!dev || n->dev == dev) {
85762306a36Sopenharmony_ci				*np = n->next;
85862306a36Sopenharmony_ci				n->next = freelist;
85962306a36Sopenharmony_ci				freelist = n;
86062306a36Sopenharmony_ci				continue;
86162306a36Sopenharmony_ci			}
86262306a36Sopenharmony_ci			np = &n->next;
86362306a36Sopenharmony_ci		}
86462306a36Sopenharmony_ci	}
86562306a36Sopenharmony_ci	write_unlock_bh(&tbl->lock);
86662306a36Sopenharmony_ci	while ((n = freelist)) {
86762306a36Sopenharmony_ci		freelist = n->next;
86862306a36Sopenharmony_ci		n->next = NULL;
86962306a36Sopenharmony_ci		if (tbl->pdestructor)
87062306a36Sopenharmony_ci			tbl->pdestructor(n);
87162306a36Sopenharmony_ci		netdev_put(n->dev, &n->dev_tracker);
87262306a36Sopenharmony_ci		kfree(n);
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci	return -ENOENT;
87562306a36Sopenharmony_ci}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_cistatic void neigh_parms_destroy(struct neigh_parms *parms);
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cistatic inline void neigh_parms_put(struct neigh_parms *parms)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	if (refcount_dec_and_test(&parms->refcnt))
88262306a36Sopenharmony_ci		neigh_parms_destroy(parms);
88362306a36Sopenharmony_ci}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci/*
88662306a36Sopenharmony_ci *	neighbour must already be out of the table;
88762306a36Sopenharmony_ci *
88862306a36Sopenharmony_ci */
88962306a36Sopenharmony_civoid neigh_destroy(struct neighbour *neigh)
89062306a36Sopenharmony_ci{
89162306a36Sopenharmony_ci	struct net_device *dev = neigh->dev;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	NEIGH_CACHE_STAT_INC(neigh->tbl, destroys);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	if (!neigh->dead) {
89662306a36Sopenharmony_ci		pr_warn("Destroying alive neighbour %p\n", neigh);
89762306a36Sopenharmony_ci		dump_stack();
89862306a36Sopenharmony_ci		return;
89962306a36Sopenharmony_ci	}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	if (neigh_del_timer(neigh))
90262306a36Sopenharmony_ci		pr_warn("Impossible event\n");
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	write_lock_bh(&neigh->lock);
90562306a36Sopenharmony_ci	__skb_queue_purge(&neigh->arp_queue);
90662306a36Sopenharmony_ci	write_unlock_bh(&neigh->lock);
90762306a36Sopenharmony_ci	neigh->arp_queue_len_bytes = 0;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	if (dev->netdev_ops->ndo_neigh_destroy)
91062306a36Sopenharmony_ci		dev->netdev_ops->ndo_neigh_destroy(dev, neigh);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	netdev_put(dev, &neigh->dev_tracker);
91362306a36Sopenharmony_ci	neigh_parms_put(neigh->parms);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	neigh_dbg(2, "neigh %p is destroyed\n", neigh);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	atomic_dec(&neigh->tbl->entries);
91862306a36Sopenharmony_ci	kfree_rcu(neigh, rcu);
91962306a36Sopenharmony_ci}
92062306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_destroy);
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci/* Neighbour state is suspicious;
92362306a36Sopenharmony_ci   disable fast path.
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci   Called with write_locked neigh.
92662306a36Sopenharmony_ci */
92762306a36Sopenharmony_cistatic void neigh_suspect(struct neighbour *neigh)
92862306a36Sopenharmony_ci{
92962306a36Sopenharmony_ci	neigh_dbg(2, "neigh %p is suspected\n", neigh);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	WRITE_ONCE(neigh->output, neigh->ops->output);
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci/* Neighbour state is OK;
93562306a36Sopenharmony_ci   enable fast path.
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci   Called with write_locked neigh.
93862306a36Sopenharmony_ci */
93962306a36Sopenharmony_cistatic void neigh_connect(struct neighbour *neigh)
94062306a36Sopenharmony_ci{
94162306a36Sopenharmony_ci	neigh_dbg(2, "neigh %p is connected\n", neigh);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	WRITE_ONCE(neigh->output, neigh->ops->connected_output);
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_cistatic void neigh_periodic_work(struct work_struct *work)
94762306a36Sopenharmony_ci{
94862306a36Sopenharmony_ci	struct neigh_table *tbl = container_of(work, struct neigh_table, gc_work.work);
94962306a36Sopenharmony_ci	struct neighbour *n;
95062306a36Sopenharmony_ci	struct neighbour __rcu **np;
95162306a36Sopenharmony_ci	unsigned int i;
95262306a36Sopenharmony_ci	struct neigh_hash_table *nht;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	NEIGH_CACHE_STAT_INC(tbl, periodic_gc_runs);
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	write_lock_bh(&tbl->lock);
95762306a36Sopenharmony_ci	nht = rcu_dereference_protected(tbl->nht,
95862306a36Sopenharmony_ci					lockdep_is_held(&tbl->lock));
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	/*
96162306a36Sopenharmony_ci	 *	periodically recompute ReachableTime from random function
96262306a36Sopenharmony_ci	 */
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	if (time_after(jiffies, tbl->last_rand + 300 * HZ)) {
96562306a36Sopenharmony_ci		struct neigh_parms *p;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci		WRITE_ONCE(tbl->last_rand, jiffies);
96862306a36Sopenharmony_ci		list_for_each_entry(p, &tbl->parms_list, list)
96962306a36Sopenharmony_ci			p->reachable_time =
97062306a36Sopenharmony_ci				neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME));
97162306a36Sopenharmony_ci	}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	if (atomic_read(&tbl->entries) < READ_ONCE(tbl->gc_thresh1))
97462306a36Sopenharmony_ci		goto out;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	for (i = 0 ; i < (1 << nht->hash_shift); i++) {
97762306a36Sopenharmony_ci		np = &nht->hash_buckets[i];
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci		while ((n = rcu_dereference_protected(*np,
98062306a36Sopenharmony_ci				lockdep_is_held(&tbl->lock))) != NULL) {
98162306a36Sopenharmony_ci			unsigned int state;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci			write_lock(&n->lock);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci			state = n->nud_state;
98662306a36Sopenharmony_ci			if ((state & (NUD_PERMANENT | NUD_IN_TIMER)) ||
98762306a36Sopenharmony_ci			    (n->flags & NTF_EXT_LEARNED)) {
98862306a36Sopenharmony_ci				write_unlock(&n->lock);
98962306a36Sopenharmony_ci				goto next_elt;
99062306a36Sopenharmony_ci			}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci			if (time_before(n->used, n->confirmed) &&
99362306a36Sopenharmony_ci			    time_is_before_eq_jiffies(n->confirmed))
99462306a36Sopenharmony_ci				n->used = n->confirmed;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci			if (refcount_read(&n->refcnt) == 1 &&
99762306a36Sopenharmony_ci			    (state == NUD_FAILED ||
99862306a36Sopenharmony_ci			     !time_in_range_open(jiffies, n->used,
99962306a36Sopenharmony_ci						 n->used + NEIGH_VAR(n->parms, GC_STALETIME)))) {
100062306a36Sopenharmony_ci				rcu_assign_pointer(*np,
100162306a36Sopenharmony_ci					rcu_dereference_protected(n->next,
100262306a36Sopenharmony_ci						lockdep_is_held(&tbl->lock)));
100362306a36Sopenharmony_ci				neigh_mark_dead(n);
100462306a36Sopenharmony_ci				write_unlock(&n->lock);
100562306a36Sopenharmony_ci				neigh_cleanup_and_release(n);
100662306a36Sopenharmony_ci				continue;
100762306a36Sopenharmony_ci			}
100862306a36Sopenharmony_ci			write_unlock(&n->lock);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_cinext_elt:
101162306a36Sopenharmony_ci			np = &n->next;
101262306a36Sopenharmony_ci		}
101362306a36Sopenharmony_ci		/*
101462306a36Sopenharmony_ci		 * It's fine to release lock here, even if hash table
101562306a36Sopenharmony_ci		 * grows while we are preempted.
101662306a36Sopenharmony_ci		 */
101762306a36Sopenharmony_ci		write_unlock_bh(&tbl->lock);
101862306a36Sopenharmony_ci		cond_resched();
101962306a36Sopenharmony_ci		write_lock_bh(&tbl->lock);
102062306a36Sopenharmony_ci		nht = rcu_dereference_protected(tbl->nht,
102162306a36Sopenharmony_ci						lockdep_is_held(&tbl->lock));
102262306a36Sopenharmony_ci	}
102362306a36Sopenharmony_ciout:
102462306a36Sopenharmony_ci	/* Cycle through all hash buckets every BASE_REACHABLE_TIME/2 ticks.
102562306a36Sopenharmony_ci	 * ARP entry timeouts range from 1/2 BASE_REACHABLE_TIME to 3/2
102662306a36Sopenharmony_ci	 * BASE_REACHABLE_TIME.
102762306a36Sopenharmony_ci	 */
102862306a36Sopenharmony_ci	queue_delayed_work(system_power_efficient_wq, &tbl->gc_work,
102962306a36Sopenharmony_ci			      NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME) >> 1);
103062306a36Sopenharmony_ci	write_unlock_bh(&tbl->lock);
103162306a36Sopenharmony_ci}
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_cistatic __inline__ int neigh_max_probes(struct neighbour *n)
103462306a36Sopenharmony_ci{
103562306a36Sopenharmony_ci	struct neigh_parms *p = n->parms;
103662306a36Sopenharmony_ci	return NEIGH_VAR(p, UCAST_PROBES) + NEIGH_VAR(p, APP_PROBES) +
103762306a36Sopenharmony_ci	       (n->nud_state & NUD_PROBE ? NEIGH_VAR(p, MCAST_REPROBES) :
103862306a36Sopenharmony_ci	        NEIGH_VAR(p, MCAST_PROBES));
103962306a36Sopenharmony_ci}
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_cistatic void neigh_invalidate(struct neighbour *neigh)
104262306a36Sopenharmony_ci	__releases(neigh->lock)
104362306a36Sopenharmony_ci	__acquires(neigh->lock)
104462306a36Sopenharmony_ci{
104562306a36Sopenharmony_ci	struct sk_buff *skb;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	NEIGH_CACHE_STAT_INC(neigh->tbl, res_failed);
104862306a36Sopenharmony_ci	neigh_dbg(2, "neigh %p is failed\n", neigh);
104962306a36Sopenharmony_ci	neigh->updated = jiffies;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	/* It is very thin place. report_unreachable is very complicated
105262306a36Sopenharmony_ci	   routine. Particularly, it can hit the same neighbour entry!
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	   So that, we try to be accurate and avoid dead loop. --ANK
105562306a36Sopenharmony_ci	 */
105662306a36Sopenharmony_ci	while (neigh->nud_state == NUD_FAILED &&
105762306a36Sopenharmony_ci	       (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) {
105862306a36Sopenharmony_ci		write_unlock(&neigh->lock);
105962306a36Sopenharmony_ci		neigh->ops->error_report(neigh, skb);
106062306a36Sopenharmony_ci		write_lock(&neigh->lock);
106162306a36Sopenharmony_ci	}
106262306a36Sopenharmony_ci	__skb_queue_purge(&neigh->arp_queue);
106362306a36Sopenharmony_ci	neigh->arp_queue_len_bytes = 0;
106462306a36Sopenharmony_ci}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_cistatic void neigh_probe(struct neighbour *neigh)
106762306a36Sopenharmony_ci	__releases(neigh->lock)
106862306a36Sopenharmony_ci{
106962306a36Sopenharmony_ci	struct sk_buff *skb = skb_peek_tail(&neigh->arp_queue);
107062306a36Sopenharmony_ci	/* keep skb alive even if arp_queue overflows */
107162306a36Sopenharmony_ci	if (skb)
107262306a36Sopenharmony_ci		skb = skb_clone(skb, GFP_ATOMIC);
107362306a36Sopenharmony_ci	write_unlock(&neigh->lock);
107462306a36Sopenharmony_ci	if (neigh->ops->solicit)
107562306a36Sopenharmony_ci		neigh->ops->solicit(neigh, skb);
107662306a36Sopenharmony_ci	atomic_inc(&neigh->probes);
107762306a36Sopenharmony_ci	consume_skb(skb);
107862306a36Sopenharmony_ci}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci/* Called when a timer expires for a neighbour entry. */
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_cistatic void neigh_timer_handler(struct timer_list *t)
108362306a36Sopenharmony_ci{
108462306a36Sopenharmony_ci	unsigned long now, next;
108562306a36Sopenharmony_ci	struct neighbour *neigh = from_timer(neigh, t, timer);
108662306a36Sopenharmony_ci	unsigned int state;
108762306a36Sopenharmony_ci	int notify = 0;
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	write_lock(&neigh->lock);
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	state = neigh->nud_state;
109262306a36Sopenharmony_ci	now = jiffies;
109362306a36Sopenharmony_ci	next = now + HZ;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	if (!(state & NUD_IN_TIMER))
109662306a36Sopenharmony_ci		goto out;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (state & NUD_REACHABLE) {
109962306a36Sopenharmony_ci		if (time_before_eq(now,
110062306a36Sopenharmony_ci				   neigh->confirmed + neigh->parms->reachable_time)) {
110162306a36Sopenharmony_ci			neigh_dbg(2, "neigh %p is still alive\n", neigh);
110262306a36Sopenharmony_ci			next = neigh->confirmed + neigh->parms->reachable_time;
110362306a36Sopenharmony_ci		} else if (time_before_eq(now,
110462306a36Sopenharmony_ci					  neigh->used +
110562306a36Sopenharmony_ci					  NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) {
110662306a36Sopenharmony_ci			neigh_dbg(2, "neigh %p is delayed\n", neigh);
110762306a36Sopenharmony_ci			WRITE_ONCE(neigh->nud_state, NUD_DELAY);
110862306a36Sopenharmony_ci			neigh->updated = jiffies;
110962306a36Sopenharmony_ci			neigh_suspect(neigh);
111062306a36Sopenharmony_ci			next = now + NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME);
111162306a36Sopenharmony_ci		} else {
111262306a36Sopenharmony_ci			neigh_dbg(2, "neigh %p is suspected\n", neigh);
111362306a36Sopenharmony_ci			WRITE_ONCE(neigh->nud_state, NUD_STALE);
111462306a36Sopenharmony_ci			neigh->updated = jiffies;
111562306a36Sopenharmony_ci			neigh_suspect(neigh);
111662306a36Sopenharmony_ci			notify = 1;
111762306a36Sopenharmony_ci		}
111862306a36Sopenharmony_ci	} else if (state & NUD_DELAY) {
111962306a36Sopenharmony_ci		if (time_before_eq(now,
112062306a36Sopenharmony_ci				   neigh->confirmed +
112162306a36Sopenharmony_ci				   NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) {
112262306a36Sopenharmony_ci			neigh_dbg(2, "neigh %p is now reachable\n", neigh);
112362306a36Sopenharmony_ci			WRITE_ONCE(neigh->nud_state, NUD_REACHABLE);
112462306a36Sopenharmony_ci			neigh->updated = jiffies;
112562306a36Sopenharmony_ci			neigh_connect(neigh);
112662306a36Sopenharmony_ci			notify = 1;
112762306a36Sopenharmony_ci			next = neigh->confirmed + neigh->parms->reachable_time;
112862306a36Sopenharmony_ci		} else {
112962306a36Sopenharmony_ci			neigh_dbg(2, "neigh %p is probed\n", neigh);
113062306a36Sopenharmony_ci			WRITE_ONCE(neigh->nud_state, NUD_PROBE);
113162306a36Sopenharmony_ci			neigh->updated = jiffies;
113262306a36Sopenharmony_ci			atomic_set(&neigh->probes, 0);
113362306a36Sopenharmony_ci			notify = 1;
113462306a36Sopenharmony_ci			next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME),
113562306a36Sopenharmony_ci					 HZ/100);
113662306a36Sopenharmony_ci		}
113762306a36Sopenharmony_ci	} else {
113862306a36Sopenharmony_ci		/* NUD_PROBE|NUD_INCOMPLETE */
113962306a36Sopenharmony_ci		next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), HZ/100);
114062306a36Sopenharmony_ci	}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	if ((neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) &&
114362306a36Sopenharmony_ci	    atomic_read(&neigh->probes) >= neigh_max_probes(neigh)) {
114462306a36Sopenharmony_ci		WRITE_ONCE(neigh->nud_state, NUD_FAILED);
114562306a36Sopenharmony_ci		notify = 1;
114662306a36Sopenharmony_ci		neigh_invalidate(neigh);
114762306a36Sopenharmony_ci		goto out;
114862306a36Sopenharmony_ci	}
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	if (neigh->nud_state & NUD_IN_TIMER) {
115162306a36Sopenharmony_ci		if (time_before(next, jiffies + HZ/100))
115262306a36Sopenharmony_ci			next = jiffies + HZ/100;
115362306a36Sopenharmony_ci		if (!mod_timer(&neigh->timer, next))
115462306a36Sopenharmony_ci			neigh_hold(neigh);
115562306a36Sopenharmony_ci	}
115662306a36Sopenharmony_ci	if (neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) {
115762306a36Sopenharmony_ci		neigh_probe(neigh);
115862306a36Sopenharmony_ci	} else {
115962306a36Sopenharmony_ciout:
116062306a36Sopenharmony_ci		write_unlock(&neigh->lock);
116162306a36Sopenharmony_ci	}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	if (notify)
116462306a36Sopenharmony_ci		neigh_update_notify(neigh, 0);
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	trace_neigh_timer_handler(neigh, 0);
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	neigh_release(neigh);
116962306a36Sopenharmony_ci}
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ciint __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb,
117262306a36Sopenharmony_ci		       const bool immediate_ok)
117362306a36Sopenharmony_ci{
117462306a36Sopenharmony_ci	int rc;
117562306a36Sopenharmony_ci	bool immediate_probe = false;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	write_lock_bh(&neigh->lock);
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	rc = 0;
118062306a36Sopenharmony_ci	if (neigh->nud_state & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE))
118162306a36Sopenharmony_ci		goto out_unlock_bh;
118262306a36Sopenharmony_ci	if (neigh->dead)
118362306a36Sopenharmony_ci		goto out_dead;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) {
118662306a36Sopenharmony_ci		if (NEIGH_VAR(neigh->parms, MCAST_PROBES) +
118762306a36Sopenharmony_ci		    NEIGH_VAR(neigh->parms, APP_PROBES)) {
118862306a36Sopenharmony_ci			unsigned long next, now = jiffies;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci			atomic_set(&neigh->probes,
119162306a36Sopenharmony_ci				   NEIGH_VAR(neigh->parms, UCAST_PROBES));
119262306a36Sopenharmony_ci			neigh_del_timer(neigh);
119362306a36Sopenharmony_ci			WRITE_ONCE(neigh->nud_state, NUD_INCOMPLETE);
119462306a36Sopenharmony_ci			neigh->updated = now;
119562306a36Sopenharmony_ci			if (!immediate_ok) {
119662306a36Sopenharmony_ci				next = now + 1;
119762306a36Sopenharmony_ci			} else {
119862306a36Sopenharmony_ci				immediate_probe = true;
119962306a36Sopenharmony_ci				next = now + max(NEIGH_VAR(neigh->parms,
120062306a36Sopenharmony_ci							   RETRANS_TIME),
120162306a36Sopenharmony_ci						 HZ / 100);
120262306a36Sopenharmony_ci			}
120362306a36Sopenharmony_ci			neigh_add_timer(neigh, next);
120462306a36Sopenharmony_ci		} else {
120562306a36Sopenharmony_ci			WRITE_ONCE(neigh->nud_state, NUD_FAILED);
120662306a36Sopenharmony_ci			neigh->updated = jiffies;
120762306a36Sopenharmony_ci			write_unlock_bh(&neigh->lock);
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci			kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_FAILED);
121062306a36Sopenharmony_ci			return 1;
121162306a36Sopenharmony_ci		}
121262306a36Sopenharmony_ci	} else if (neigh->nud_state & NUD_STALE) {
121362306a36Sopenharmony_ci		neigh_dbg(2, "neigh %p is delayed\n", neigh);
121462306a36Sopenharmony_ci		neigh_del_timer(neigh);
121562306a36Sopenharmony_ci		WRITE_ONCE(neigh->nud_state, NUD_DELAY);
121662306a36Sopenharmony_ci		neigh->updated = jiffies;
121762306a36Sopenharmony_ci		neigh_add_timer(neigh, jiffies +
121862306a36Sopenharmony_ci				NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME));
121962306a36Sopenharmony_ci	}
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	if (neigh->nud_state == NUD_INCOMPLETE) {
122262306a36Sopenharmony_ci		if (skb) {
122362306a36Sopenharmony_ci			while (neigh->arp_queue_len_bytes + skb->truesize >
122462306a36Sopenharmony_ci			       NEIGH_VAR(neigh->parms, QUEUE_LEN_BYTES)) {
122562306a36Sopenharmony_ci				struct sk_buff *buff;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci				buff = __skb_dequeue(&neigh->arp_queue);
122862306a36Sopenharmony_ci				if (!buff)
122962306a36Sopenharmony_ci					break;
123062306a36Sopenharmony_ci				neigh->arp_queue_len_bytes -= buff->truesize;
123162306a36Sopenharmony_ci				kfree_skb_reason(buff, SKB_DROP_REASON_NEIGH_QUEUEFULL);
123262306a36Sopenharmony_ci				NEIGH_CACHE_STAT_INC(neigh->tbl, unres_discards);
123362306a36Sopenharmony_ci			}
123462306a36Sopenharmony_ci			skb_dst_force(skb);
123562306a36Sopenharmony_ci			__skb_queue_tail(&neigh->arp_queue, skb);
123662306a36Sopenharmony_ci			neigh->arp_queue_len_bytes += skb->truesize;
123762306a36Sopenharmony_ci		}
123862306a36Sopenharmony_ci		rc = 1;
123962306a36Sopenharmony_ci	}
124062306a36Sopenharmony_ciout_unlock_bh:
124162306a36Sopenharmony_ci	if (immediate_probe)
124262306a36Sopenharmony_ci		neigh_probe(neigh);
124362306a36Sopenharmony_ci	else
124462306a36Sopenharmony_ci		write_unlock(&neigh->lock);
124562306a36Sopenharmony_ci	local_bh_enable();
124662306a36Sopenharmony_ci	trace_neigh_event_send_done(neigh, rc);
124762306a36Sopenharmony_ci	return rc;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ciout_dead:
125062306a36Sopenharmony_ci	if (neigh->nud_state & NUD_STALE)
125162306a36Sopenharmony_ci		goto out_unlock_bh;
125262306a36Sopenharmony_ci	write_unlock_bh(&neigh->lock);
125362306a36Sopenharmony_ci	kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_DEAD);
125462306a36Sopenharmony_ci	trace_neigh_event_send_dead(neigh, 1);
125562306a36Sopenharmony_ci	return 1;
125662306a36Sopenharmony_ci}
125762306a36Sopenharmony_ciEXPORT_SYMBOL(__neigh_event_send);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_cistatic void neigh_update_hhs(struct neighbour *neigh)
126062306a36Sopenharmony_ci{
126162306a36Sopenharmony_ci	struct hh_cache *hh;
126262306a36Sopenharmony_ci	void (*update)(struct hh_cache*, const struct net_device*, const unsigned char *)
126362306a36Sopenharmony_ci		= NULL;
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	if (neigh->dev->header_ops)
126662306a36Sopenharmony_ci		update = neigh->dev->header_ops->cache_update;
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	if (update) {
126962306a36Sopenharmony_ci		hh = &neigh->hh;
127062306a36Sopenharmony_ci		if (READ_ONCE(hh->hh_len)) {
127162306a36Sopenharmony_ci			write_seqlock_bh(&hh->hh_lock);
127262306a36Sopenharmony_ci			update(hh, neigh->dev, neigh->ha);
127362306a36Sopenharmony_ci			write_sequnlock_bh(&hh->hh_lock);
127462306a36Sopenharmony_ci		}
127562306a36Sopenharmony_ci	}
127662306a36Sopenharmony_ci}
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci/* Generic update routine.
127962306a36Sopenharmony_ci   -- lladdr is new lladdr or NULL, if it is not supplied.
128062306a36Sopenharmony_ci   -- new    is new state.
128162306a36Sopenharmony_ci   -- flags
128262306a36Sopenharmony_ci	NEIGH_UPDATE_F_OVERRIDE allows to override existing lladdr,
128362306a36Sopenharmony_ci				if it is different.
128462306a36Sopenharmony_ci	NEIGH_UPDATE_F_WEAK_OVERRIDE will suspect existing "connected"
128562306a36Sopenharmony_ci				lladdr instead of overriding it
128662306a36Sopenharmony_ci				if it is different.
128762306a36Sopenharmony_ci	NEIGH_UPDATE_F_ADMIN	means that the change is administrative.
128862306a36Sopenharmony_ci	NEIGH_UPDATE_F_USE	means that the entry is user triggered.
128962306a36Sopenharmony_ci	NEIGH_UPDATE_F_MANAGED	means that the entry will be auto-refreshed.
129062306a36Sopenharmony_ci	NEIGH_UPDATE_F_OVERRIDE_ISROUTER allows to override existing
129162306a36Sopenharmony_ci				NTF_ROUTER flag.
129262306a36Sopenharmony_ci	NEIGH_UPDATE_F_ISROUTER	indicates if the neighbour is known as
129362306a36Sopenharmony_ci				a router.
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci   Caller MUST hold reference count on the entry.
129662306a36Sopenharmony_ci */
129762306a36Sopenharmony_cistatic int __neigh_update(struct neighbour *neigh, const u8 *lladdr,
129862306a36Sopenharmony_ci			  u8 new, u32 flags, u32 nlmsg_pid,
129962306a36Sopenharmony_ci			  struct netlink_ext_ack *extack)
130062306a36Sopenharmony_ci{
130162306a36Sopenharmony_ci	bool gc_update = false, managed_update = false;
130262306a36Sopenharmony_ci	int update_isrouter = 0;
130362306a36Sopenharmony_ci	struct net_device *dev;
130462306a36Sopenharmony_ci	int err, notify = 0;
130562306a36Sopenharmony_ci	u8 old;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	trace_neigh_update(neigh, lladdr, new, flags, nlmsg_pid);
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	write_lock_bh(&neigh->lock);
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	dev    = neigh->dev;
131262306a36Sopenharmony_ci	old    = neigh->nud_state;
131362306a36Sopenharmony_ci	err    = -EPERM;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	if (neigh->dead) {
131662306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Neighbor entry is now dead");
131762306a36Sopenharmony_ci		new = old;
131862306a36Sopenharmony_ci		goto out;
131962306a36Sopenharmony_ci	}
132062306a36Sopenharmony_ci	if (!(flags & NEIGH_UPDATE_F_ADMIN) &&
132162306a36Sopenharmony_ci	    (old & (NUD_NOARP | NUD_PERMANENT)))
132262306a36Sopenharmony_ci		goto out;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	neigh_update_flags(neigh, flags, &notify, &gc_update, &managed_update);
132562306a36Sopenharmony_ci	if (flags & (NEIGH_UPDATE_F_USE | NEIGH_UPDATE_F_MANAGED)) {
132662306a36Sopenharmony_ci		new = old & ~NUD_PERMANENT;
132762306a36Sopenharmony_ci		WRITE_ONCE(neigh->nud_state, new);
132862306a36Sopenharmony_ci		err = 0;
132962306a36Sopenharmony_ci		goto out;
133062306a36Sopenharmony_ci	}
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	if (!(new & NUD_VALID)) {
133362306a36Sopenharmony_ci		neigh_del_timer(neigh);
133462306a36Sopenharmony_ci		if (old & NUD_CONNECTED)
133562306a36Sopenharmony_ci			neigh_suspect(neigh);
133662306a36Sopenharmony_ci		WRITE_ONCE(neigh->nud_state, new);
133762306a36Sopenharmony_ci		err = 0;
133862306a36Sopenharmony_ci		notify = old & NUD_VALID;
133962306a36Sopenharmony_ci		if ((old & (NUD_INCOMPLETE | NUD_PROBE)) &&
134062306a36Sopenharmony_ci		    (new & NUD_FAILED)) {
134162306a36Sopenharmony_ci			neigh_invalidate(neigh);
134262306a36Sopenharmony_ci			notify = 1;
134362306a36Sopenharmony_ci		}
134462306a36Sopenharmony_ci		goto out;
134562306a36Sopenharmony_ci	}
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	/* Compare new lladdr with cached one */
134862306a36Sopenharmony_ci	if (!dev->addr_len) {
134962306a36Sopenharmony_ci		/* First case: device needs no address. */
135062306a36Sopenharmony_ci		lladdr = neigh->ha;
135162306a36Sopenharmony_ci	} else if (lladdr) {
135262306a36Sopenharmony_ci		/* The second case: if something is already cached
135362306a36Sopenharmony_ci		   and a new address is proposed:
135462306a36Sopenharmony_ci		   - compare new & old
135562306a36Sopenharmony_ci		   - if they are different, check override flag
135662306a36Sopenharmony_ci		 */
135762306a36Sopenharmony_ci		if ((old & NUD_VALID) &&
135862306a36Sopenharmony_ci		    !memcmp(lladdr, neigh->ha, dev->addr_len))
135962306a36Sopenharmony_ci			lladdr = neigh->ha;
136062306a36Sopenharmony_ci	} else {
136162306a36Sopenharmony_ci		/* No address is supplied; if we know something,
136262306a36Sopenharmony_ci		   use it, otherwise discard the request.
136362306a36Sopenharmony_ci		 */
136462306a36Sopenharmony_ci		err = -EINVAL;
136562306a36Sopenharmony_ci		if (!(old & NUD_VALID)) {
136662306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "No link layer address given");
136762306a36Sopenharmony_ci			goto out;
136862306a36Sopenharmony_ci		}
136962306a36Sopenharmony_ci		lladdr = neigh->ha;
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	/* Update confirmed timestamp for neighbour entry after we
137362306a36Sopenharmony_ci	 * received ARP packet even if it doesn't change IP to MAC binding.
137462306a36Sopenharmony_ci	 */
137562306a36Sopenharmony_ci	if (new & NUD_CONNECTED)
137662306a36Sopenharmony_ci		neigh->confirmed = jiffies;
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	/* If entry was valid and address is not changed,
137962306a36Sopenharmony_ci	   do not change entry state, if new one is STALE.
138062306a36Sopenharmony_ci	 */
138162306a36Sopenharmony_ci	err = 0;
138262306a36Sopenharmony_ci	update_isrouter = flags & NEIGH_UPDATE_F_OVERRIDE_ISROUTER;
138362306a36Sopenharmony_ci	if (old & NUD_VALID) {
138462306a36Sopenharmony_ci		if (lladdr != neigh->ha && !(flags & NEIGH_UPDATE_F_OVERRIDE)) {
138562306a36Sopenharmony_ci			update_isrouter = 0;
138662306a36Sopenharmony_ci			if ((flags & NEIGH_UPDATE_F_WEAK_OVERRIDE) &&
138762306a36Sopenharmony_ci			    (old & NUD_CONNECTED)) {
138862306a36Sopenharmony_ci				lladdr = neigh->ha;
138962306a36Sopenharmony_ci				new = NUD_STALE;
139062306a36Sopenharmony_ci			} else
139162306a36Sopenharmony_ci				goto out;
139262306a36Sopenharmony_ci		} else {
139362306a36Sopenharmony_ci			if (lladdr == neigh->ha && new == NUD_STALE &&
139462306a36Sopenharmony_ci			    !(flags & NEIGH_UPDATE_F_ADMIN))
139562306a36Sopenharmony_ci				new = old;
139662306a36Sopenharmony_ci		}
139762306a36Sopenharmony_ci	}
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	/* Update timestamp only once we know we will make a change to the
140062306a36Sopenharmony_ci	 * neighbour entry. Otherwise we risk to move the locktime window with
140162306a36Sopenharmony_ci	 * noop updates and ignore relevant ARP updates.
140262306a36Sopenharmony_ci	 */
140362306a36Sopenharmony_ci	if (new != old || lladdr != neigh->ha)
140462306a36Sopenharmony_ci		neigh->updated = jiffies;
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	if (new != old) {
140762306a36Sopenharmony_ci		neigh_del_timer(neigh);
140862306a36Sopenharmony_ci		if (new & NUD_PROBE)
140962306a36Sopenharmony_ci			atomic_set(&neigh->probes, 0);
141062306a36Sopenharmony_ci		if (new & NUD_IN_TIMER)
141162306a36Sopenharmony_ci			neigh_add_timer(neigh, (jiffies +
141262306a36Sopenharmony_ci						((new & NUD_REACHABLE) ?
141362306a36Sopenharmony_ci						 neigh->parms->reachable_time :
141462306a36Sopenharmony_ci						 0)));
141562306a36Sopenharmony_ci		WRITE_ONCE(neigh->nud_state, new);
141662306a36Sopenharmony_ci		notify = 1;
141762306a36Sopenharmony_ci	}
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	if (lladdr != neigh->ha) {
142062306a36Sopenharmony_ci		write_seqlock(&neigh->ha_lock);
142162306a36Sopenharmony_ci		memcpy(&neigh->ha, lladdr, dev->addr_len);
142262306a36Sopenharmony_ci		write_sequnlock(&neigh->ha_lock);
142362306a36Sopenharmony_ci		neigh_update_hhs(neigh);
142462306a36Sopenharmony_ci		if (!(new & NUD_CONNECTED))
142562306a36Sopenharmony_ci			neigh->confirmed = jiffies -
142662306a36Sopenharmony_ci				      (NEIGH_VAR(neigh->parms, BASE_REACHABLE_TIME) << 1);
142762306a36Sopenharmony_ci		notify = 1;
142862306a36Sopenharmony_ci	}
142962306a36Sopenharmony_ci	if (new == old)
143062306a36Sopenharmony_ci		goto out;
143162306a36Sopenharmony_ci	if (new & NUD_CONNECTED)
143262306a36Sopenharmony_ci		neigh_connect(neigh);
143362306a36Sopenharmony_ci	else
143462306a36Sopenharmony_ci		neigh_suspect(neigh);
143562306a36Sopenharmony_ci	if (!(old & NUD_VALID)) {
143662306a36Sopenharmony_ci		struct sk_buff *skb;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci		/* Again: avoid dead loop if something went wrong */
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci		while (neigh->nud_state & NUD_VALID &&
144162306a36Sopenharmony_ci		       (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) {
144262306a36Sopenharmony_ci			struct dst_entry *dst = skb_dst(skb);
144362306a36Sopenharmony_ci			struct neighbour *n2, *n1 = neigh;
144462306a36Sopenharmony_ci			write_unlock_bh(&neigh->lock);
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci			rcu_read_lock();
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_ci			/* Why not just use 'neigh' as-is?  The problem is that
144962306a36Sopenharmony_ci			 * things such as shaper, eql, and sch_teql can end up
145062306a36Sopenharmony_ci			 * using alternative, different, neigh objects to output
145162306a36Sopenharmony_ci			 * the packet in the output path.  So what we need to do
145262306a36Sopenharmony_ci			 * here is re-lookup the top-level neigh in the path so
145362306a36Sopenharmony_ci			 * we can reinject the packet there.
145462306a36Sopenharmony_ci			 */
145562306a36Sopenharmony_ci			n2 = NULL;
145662306a36Sopenharmony_ci			if (dst && dst->obsolete != DST_OBSOLETE_DEAD) {
145762306a36Sopenharmony_ci				n2 = dst_neigh_lookup_skb(dst, skb);
145862306a36Sopenharmony_ci				if (n2)
145962306a36Sopenharmony_ci					n1 = n2;
146062306a36Sopenharmony_ci			}
146162306a36Sopenharmony_ci			READ_ONCE(n1->output)(n1, skb);
146262306a36Sopenharmony_ci			if (n2)
146362306a36Sopenharmony_ci				neigh_release(n2);
146462306a36Sopenharmony_ci			rcu_read_unlock();
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci			write_lock_bh(&neigh->lock);
146762306a36Sopenharmony_ci		}
146862306a36Sopenharmony_ci		__skb_queue_purge(&neigh->arp_queue);
146962306a36Sopenharmony_ci		neigh->arp_queue_len_bytes = 0;
147062306a36Sopenharmony_ci	}
147162306a36Sopenharmony_ciout:
147262306a36Sopenharmony_ci	if (update_isrouter)
147362306a36Sopenharmony_ci		neigh_update_is_router(neigh, flags, &notify);
147462306a36Sopenharmony_ci	write_unlock_bh(&neigh->lock);
147562306a36Sopenharmony_ci	if (((new ^ old) & NUD_PERMANENT) || gc_update)
147662306a36Sopenharmony_ci		neigh_update_gc_list(neigh);
147762306a36Sopenharmony_ci	if (managed_update)
147862306a36Sopenharmony_ci		neigh_update_managed_list(neigh);
147962306a36Sopenharmony_ci	if (notify)
148062306a36Sopenharmony_ci		neigh_update_notify(neigh, nlmsg_pid);
148162306a36Sopenharmony_ci	trace_neigh_update_done(neigh, err);
148262306a36Sopenharmony_ci	return err;
148362306a36Sopenharmony_ci}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ciint neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
148662306a36Sopenharmony_ci		 u32 flags, u32 nlmsg_pid)
148762306a36Sopenharmony_ci{
148862306a36Sopenharmony_ci	return __neigh_update(neigh, lladdr, new, flags, nlmsg_pid, NULL);
148962306a36Sopenharmony_ci}
149062306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_update);
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci/* Update the neigh to listen temporarily for probe responses, even if it is
149362306a36Sopenharmony_ci * in a NUD_FAILED state. The caller has to hold neigh->lock for writing.
149462306a36Sopenharmony_ci */
149562306a36Sopenharmony_civoid __neigh_set_probe_once(struct neighbour *neigh)
149662306a36Sopenharmony_ci{
149762306a36Sopenharmony_ci	if (neigh->dead)
149862306a36Sopenharmony_ci		return;
149962306a36Sopenharmony_ci	neigh->updated = jiffies;
150062306a36Sopenharmony_ci	if (!(neigh->nud_state & NUD_FAILED))
150162306a36Sopenharmony_ci		return;
150262306a36Sopenharmony_ci	WRITE_ONCE(neigh->nud_state, NUD_INCOMPLETE);
150362306a36Sopenharmony_ci	atomic_set(&neigh->probes, neigh_max_probes(neigh));
150462306a36Sopenharmony_ci	neigh_add_timer(neigh,
150562306a36Sopenharmony_ci			jiffies + max(NEIGH_VAR(neigh->parms, RETRANS_TIME),
150662306a36Sopenharmony_ci				      HZ/100));
150762306a36Sopenharmony_ci}
150862306a36Sopenharmony_ciEXPORT_SYMBOL(__neigh_set_probe_once);
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_cistruct neighbour *neigh_event_ns(struct neigh_table *tbl,
151162306a36Sopenharmony_ci				 u8 *lladdr, void *saddr,
151262306a36Sopenharmony_ci				 struct net_device *dev)
151362306a36Sopenharmony_ci{
151462306a36Sopenharmony_ci	struct neighbour *neigh = __neigh_lookup(tbl, saddr, dev,
151562306a36Sopenharmony_ci						 lladdr || !dev->addr_len);
151662306a36Sopenharmony_ci	if (neigh)
151762306a36Sopenharmony_ci		neigh_update(neigh, lladdr, NUD_STALE,
151862306a36Sopenharmony_ci			     NEIGH_UPDATE_F_OVERRIDE, 0);
151962306a36Sopenharmony_ci	return neigh;
152062306a36Sopenharmony_ci}
152162306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_event_ns);
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci/* called with read_lock_bh(&n->lock); */
152462306a36Sopenharmony_cistatic void neigh_hh_init(struct neighbour *n)
152562306a36Sopenharmony_ci{
152662306a36Sopenharmony_ci	struct net_device *dev = n->dev;
152762306a36Sopenharmony_ci	__be16 prot = n->tbl->protocol;
152862306a36Sopenharmony_ci	struct hh_cache	*hh = &n->hh;
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	write_lock_bh(&n->lock);
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	/* Only one thread can come in here and initialize the
153362306a36Sopenharmony_ci	 * hh_cache entry.
153462306a36Sopenharmony_ci	 */
153562306a36Sopenharmony_ci	if (!hh->hh_len)
153662306a36Sopenharmony_ci		dev->header_ops->cache(n, hh, prot);
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	write_unlock_bh(&n->lock);
153962306a36Sopenharmony_ci}
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci/* Slow and careful. */
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ciint neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb)
154462306a36Sopenharmony_ci{
154562306a36Sopenharmony_ci	int rc = 0;
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	if (!neigh_event_send(neigh, skb)) {
154862306a36Sopenharmony_ci		int err;
154962306a36Sopenharmony_ci		struct net_device *dev = neigh->dev;
155062306a36Sopenharmony_ci		unsigned int seq;
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci		if (dev->header_ops->cache && !READ_ONCE(neigh->hh.hh_len))
155362306a36Sopenharmony_ci			neigh_hh_init(neigh);
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci		do {
155662306a36Sopenharmony_ci			__skb_pull(skb, skb_network_offset(skb));
155762306a36Sopenharmony_ci			seq = read_seqbegin(&neigh->ha_lock);
155862306a36Sopenharmony_ci			err = dev_hard_header(skb, dev, ntohs(skb->protocol),
155962306a36Sopenharmony_ci					      neigh->ha, NULL, skb->len);
156062306a36Sopenharmony_ci		} while (read_seqretry(&neigh->ha_lock, seq));
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci		if (err >= 0)
156362306a36Sopenharmony_ci			rc = dev_queue_xmit(skb);
156462306a36Sopenharmony_ci		else
156562306a36Sopenharmony_ci			goto out_kfree_skb;
156662306a36Sopenharmony_ci	}
156762306a36Sopenharmony_ciout:
156862306a36Sopenharmony_ci	return rc;
156962306a36Sopenharmony_ciout_kfree_skb:
157062306a36Sopenharmony_ci	rc = -EINVAL;
157162306a36Sopenharmony_ci	kfree_skb(skb);
157262306a36Sopenharmony_ci	goto out;
157362306a36Sopenharmony_ci}
157462306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_resolve_output);
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci/* As fast as possible without hh cache */
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ciint neigh_connected_output(struct neighbour *neigh, struct sk_buff *skb)
157962306a36Sopenharmony_ci{
158062306a36Sopenharmony_ci	struct net_device *dev = neigh->dev;
158162306a36Sopenharmony_ci	unsigned int seq;
158262306a36Sopenharmony_ci	int err;
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	do {
158562306a36Sopenharmony_ci		__skb_pull(skb, skb_network_offset(skb));
158662306a36Sopenharmony_ci		seq = read_seqbegin(&neigh->ha_lock);
158762306a36Sopenharmony_ci		err = dev_hard_header(skb, dev, ntohs(skb->protocol),
158862306a36Sopenharmony_ci				      neigh->ha, NULL, skb->len);
158962306a36Sopenharmony_ci	} while (read_seqretry(&neigh->ha_lock, seq));
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	if (err >= 0)
159262306a36Sopenharmony_ci		err = dev_queue_xmit(skb);
159362306a36Sopenharmony_ci	else {
159462306a36Sopenharmony_ci		err = -EINVAL;
159562306a36Sopenharmony_ci		kfree_skb(skb);
159662306a36Sopenharmony_ci	}
159762306a36Sopenharmony_ci	return err;
159862306a36Sopenharmony_ci}
159962306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_connected_output);
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ciint neigh_direct_output(struct neighbour *neigh, struct sk_buff *skb)
160262306a36Sopenharmony_ci{
160362306a36Sopenharmony_ci	return dev_queue_xmit(skb);
160462306a36Sopenharmony_ci}
160562306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_direct_output);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_cistatic void neigh_managed_work(struct work_struct *work)
160862306a36Sopenharmony_ci{
160962306a36Sopenharmony_ci	struct neigh_table *tbl = container_of(work, struct neigh_table,
161062306a36Sopenharmony_ci					       managed_work.work);
161162306a36Sopenharmony_ci	struct neighbour *neigh;
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	write_lock_bh(&tbl->lock);
161462306a36Sopenharmony_ci	list_for_each_entry(neigh, &tbl->managed_list, managed_list)
161562306a36Sopenharmony_ci		neigh_event_send_probe(neigh, NULL, false);
161662306a36Sopenharmony_ci	queue_delayed_work(system_power_efficient_wq, &tbl->managed_work,
161762306a36Sopenharmony_ci			   NEIGH_VAR(&tbl->parms, INTERVAL_PROBE_TIME_MS));
161862306a36Sopenharmony_ci	write_unlock_bh(&tbl->lock);
161962306a36Sopenharmony_ci}
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_cistatic void neigh_proxy_process(struct timer_list *t)
162262306a36Sopenharmony_ci{
162362306a36Sopenharmony_ci	struct neigh_table *tbl = from_timer(tbl, t, proxy_timer);
162462306a36Sopenharmony_ci	long sched_next = 0;
162562306a36Sopenharmony_ci	unsigned long now = jiffies;
162662306a36Sopenharmony_ci	struct sk_buff *skb, *n;
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	spin_lock(&tbl->proxy_queue.lock);
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	skb_queue_walk_safe(&tbl->proxy_queue, skb, n) {
163162306a36Sopenharmony_ci		long tdif = NEIGH_CB(skb)->sched_next - now;
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci		if (tdif <= 0) {
163462306a36Sopenharmony_ci			struct net_device *dev = skb->dev;
163562306a36Sopenharmony_ci
163662306a36Sopenharmony_ci			neigh_parms_qlen_dec(dev, tbl->family);
163762306a36Sopenharmony_ci			__skb_unlink(skb, &tbl->proxy_queue);
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci			if (tbl->proxy_redo && netif_running(dev)) {
164062306a36Sopenharmony_ci				rcu_read_lock();
164162306a36Sopenharmony_ci				tbl->proxy_redo(skb);
164262306a36Sopenharmony_ci				rcu_read_unlock();
164362306a36Sopenharmony_ci			} else {
164462306a36Sopenharmony_ci				kfree_skb(skb);
164562306a36Sopenharmony_ci			}
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci			dev_put(dev);
164862306a36Sopenharmony_ci		} else if (!sched_next || tdif < sched_next)
164962306a36Sopenharmony_ci			sched_next = tdif;
165062306a36Sopenharmony_ci	}
165162306a36Sopenharmony_ci	del_timer(&tbl->proxy_timer);
165262306a36Sopenharmony_ci	if (sched_next)
165362306a36Sopenharmony_ci		mod_timer(&tbl->proxy_timer, jiffies + sched_next);
165462306a36Sopenharmony_ci	spin_unlock(&tbl->proxy_queue.lock);
165562306a36Sopenharmony_ci}
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_cistatic unsigned long neigh_proxy_delay(struct neigh_parms *p)
165862306a36Sopenharmony_ci{
165962306a36Sopenharmony_ci	/* If proxy_delay is zero, do not call get_random_u32_below()
166062306a36Sopenharmony_ci	 * as it is undefined behavior.
166162306a36Sopenharmony_ci	 */
166262306a36Sopenharmony_ci	unsigned long proxy_delay = NEIGH_VAR(p, PROXY_DELAY);
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	return proxy_delay ?
166562306a36Sopenharmony_ci	       jiffies + get_random_u32_below(proxy_delay) : jiffies;
166662306a36Sopenharmony_ci}
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_civoid pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
166962306a36Sopenharmony_ci		    struct sk_buff *skb)
167062306a36Sopenharmony_ci{
167162306a36Sopenharmony_ci	unsigned long sched_next = neigh_proxy_delay(p);
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	if (p->qlen > NEIGH_VAR(p, PROXY_QLEN)) {
167462306a36Sopenharmony_ci		kfree_skb(skb);
167562306a36Sopenharmony_ci		return;
167662306a36Sopenharmony_ci	}
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	NEIGH_CB(skb)->sched_next = sched_next;
167962306a36Sopenharmony_ci	NEIGH_CB(skb)->flags |= LOCALLY_ENQUEUED;
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci	spin_lock(&tbl->proxy_queue.lock);
168262306a36Sopenharmony_ci	if (del_timer(&tbl->proxy_timer)) {
168362306a36Sopenharmony_ci		if (time_before(tbl->proxy_timer.expires, sched_next))
168462306a36Sopenharmony_ci			sched_next = tbl->proxy_timer.expires;
168562306a36Sopenharmony_ci	}
168662306a36Sopenharmony_ci	skb_dst_drop(skb);
168762306a36Sopenharmony_ci	dev_hold(skb->dev);
168862306a36Sopenharmony_ci	__skb_queue_tail(&tbl->proxy_queue, skb);
168962306a36Sopenharmony_ci	p->qlen++;
169062306a36Sopenharmony_ci	mod_timer(&tbl->proxy_timer, sched_next);
169162306a36Sopenharmony_ci	spin_unlock(&tbl->proxy_queue.lock);
169262306a36Sopenharmony_ci}
169362306a36Sopenharmony_ciEXPORT_SYMBOL(pneigh_enqueue);
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_cistatic inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl,
169662306a36Sopenharmony_ci						      struct net *net, int ifindex)
169762306a36Sopenharmony_ci{
169862306a36Sopenharmony_ci	struct neigh_parms *p;
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	list_for_each_entry(p, &tbl->parms_list, list) {
170162306a36Sopenharmony_ci		if ((p->dev && p->dev->ifindex == ifindex && net_eq(neigh_parms_net(p), net)) ||
170262306a36Sopenharmony_ci		    (!p->dev && !ifindex && net_eq(net, &init_net)))
170362306a36Sopenharmony_ci			return p;
170462306a36Sopenharmony_ci	}
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	return NULL;
170762306a36Sopenharmony_ci}
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_cistruct neigh_parms *neigh_parms_alloc(struct net_device *dev,
171062306a36Sopenharmony_ci				      struct neigh_table *tbl)
171162306a36Sopenharmony_ci{
171262306a36Sopenharmony_ci	struct neigh_parms *p;
171362306a36Sopenharmony_ci	struct net *net = dev_net(dev);
171462306a36Sopenharmony_ci	const struct net_device_ops *ops = dev->netdev_ops;
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci	p = kmemdup(&tbl->parms, sizeof(*p), GFP_KERNEL);
171762306a36Sopenharmony_ci	if (p) {
171862306a36Sopenharmony_ci		p->tbl		  = tbl;
171962306a36Sopenharmony_ci		refcount_set(&p->refcnt, 1);
172062306a36Sopenharmony_ci		p->reachable_time =
172162306a36Sopenharmony_ci				neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME));
172262306a36Sopenharmony_ci		p->qlen = 0;
172362306a36Sopenharmony_ci		netdev_hold(dev, &p->dev_tracker, GFP_KERNEL);
172462306a36Sopenharmony_ci		p->dev = dev;
172562306a36Sopenharmony_ci		write_pnet(&p->net, net);
172662306a36Sopenharmony_ci		p->sysctl_table = NULL;
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci		if (ops->ndo_neigh_setup && ops->ndo_neigh_setup(dev, p)) {
172962306a36Sopenharmony_ci			netdev_put(dev, &p->dev_tracker);
173062306a36Sopenharmony_ci			kfree(p);
173162306a36Sopenharmony_ci			return NULL;
173262306a36Sopenharmony_ci		}
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci		write_lock_bh(&tbl->lock);
173562306a36Sopenharmony_ci		list_add(&p->list, &tbl->parms.list);
173662306a36Sopenharmony_ci		write_unlock_bh(&tbl->lock);
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci		neigh_parms_data_state_cleanall(p);
173962306a36Sopenharmony_ci	}
174062306a36Sopenharmony_ci	return p;
174162306a36Sopenharmony_ci}
174262306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_parms_alloc);
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_cistatic void neigh_rcu_free_parms(struct rcu_head *head)
174562306a36Sopenharmony_ci{
174662306a36Sopenharmony_ci	struct neigh_parms *parms =
174762306a36Sopenharmony_ci		container_of(head, struct neigh_parms, rcu_head);
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci	neigh_parms_put(parms);
175062306a36Sopenharmony_ci}
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_civoid neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms)
175362306a36Sopenharmony_ci{
175462306a36Sopenharmony_ci	if (!parms || parms == &tbl->parms)
175562306a36Sopenharmony_ci		return;
175662306a36Sopenharmony_ci	write_lock_bh(&tbl->lock);
175762306a36Sopenharmony_ci	list_del(&parms->list);
175862306a36Sopenharmony_ci	parms->dead = 1;
175962306a36Sopenharmony_ci	write_unlock_bh(&tbl->lock);
176062306a36Sopenharmony_ci	netdev_put(parms->dev, &parms->dev_tracker);
176162306a36Sopenharmony_ci	call_rcu(&parms->rcu_head, neigh_rcu_free_parms);
176262306a36Sopenharmony_ci}
176362306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_parms_release);
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_cistatic void neigh_parms_destroy(struct neigh_parms *parms)
176662306a36Sopenharmony_ci{
176762306a36Sopenharmony_ci	kfree(parms);
176862306a36Sopenharmony_ci}
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_cistatic struct lock_class_key neigh_table_proxy_queue_class;
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_cistatic struct neigh_table *neigh_tables[NEIGH_NR_TABLES] __read_mostly;
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_civoid neigh_table_init(int index, struct neigh_table *tbl)
177562306a36Sopenharmony_ci{
177662306a36Sopenharmony_ci	unsigned long now = jiffies;
177762306a36Sopenharmony_ci	unsigned long phsize;
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	INIT_LIST_HEAD(&tbl->parms_list);
178062306a36Sopenharmony_ci	INIT_LIST_HEAD(&tbl->gc_list);
178162306a36Sopenharmony_ci	INIT_LIST_HEAD(&tbl->managed_list);
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci	list_add(&tbl->parms.list, &tbl->parms_list);
178462306a36Sopenharmony_ci	write_pnet(&tbl->parms.net, &init_net);
178562306a36Sopenharmony_ci	refcount_set(&tbl->parms.refcnt, 1);
178662306a36Sopenharmony_ci	tbl->parms.reachable_time =
178762306a36Sopenharmony_ci			  neigh_rand_reach_time(NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME));
178862306a36Sopenharmony_ci	tbl->parms.qlen = 0;
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	tbl->stats = alloc_percpu(struct neigh_statistics);
179162306a36Sopenharmony_ci	if (!tbl->stats)
179262306a36Sopenharmony_ci		panic("cannot create neighbour cache statistics");
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
179562306a36Sopenharmony_ci	if (!proc_create_seq_data(tbl->id, 0, init_net.proc_net_stat,
179662306a36Sopenharmony_ci			      &neigh_stat_seq_ops, tbl))
179762306a36Sopenharmony_ci		panic("cannot create neighbour proc dir entry");
179862306a36Sopenharmony_ci#endif
179962306a36Sopenharmony_ci
180062306a36Sopenharmony_ci	RCU_INIT_POINTER(tbl->nht, neigh_hash_alloc(3));
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci	phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *);
180362306a36Sopenharmony_ci	tbl->phash_buckets = kzalloc(phsize, GFP_KERNEL);
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	if (!tbl->nht || !tbl->phash_buckets)
180662306a36Sopenharmony_ci		panic("cannot allocate neighbour cache hashes");
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	if (!tbl->entry_size)
180962306a36Sopenharmony_ci		tbl->entry_size = ALIGN(offsetof(struct neighbour, primary_key) +
181062306a36Sopenharmony_ci					tbl->key_len, NEIGH_PRIV_ALIGN);
181162306a36Sopenharmony_ci	else
181262306a36Sopenharmony_ci		WARN_ON(tbl->entry_size % NEIGH_PRIV_ALIGN);
181362306a36Sopenharmony_ci
181462306a36Sopenharmony_ci	rwlock_init(&tbl->lock);
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	INIT_DEFERRABLE_WORK(&tbl->gc_work, neigh_periodic_work);
181762306a36Sopenharmony_ci	queue_delayed_work(system_power_efficient_wq, &tbl->gc_work,
181862306a36Sopenharmony_ci			tbl->parms.reachable_time);
181962306a36Sopenharmony_ci	INIT_DEFERRABLE_WORK(&tbl->managed_work, neigh_managed_work);
182062306a36Sopenharmony_ci	queue_delayed_work(system_power_efficient_wq, &tbl->managed_work, 0);
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	timer_setup(&tbl->proxy_timer, neigh_proxy_process, 0);
182362306a36Sopenharmony_ci	skb_queue_head_init_class(&tbl->proxy_queue,
182462306a36Sopenharmony_ci			&neigh_table_proxy_queue_class);
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	tbl->last_flush = now;
182762306a36Sopenharmony_ci	tbl->last_rand	= now + tbl->parms.reachable_time * 20;
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci	neigh_tables[index] = tbl;
183062306a36Sopenharmony_ci}
183162306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_table_init);
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ciint neigh_table_clear(int index, struct neigh_table *tbl)
183462306a36Sopenharmony_ci{
183562306a36Sopenharmony_ci	neigh_tables[index] = NULL;
183662306a36Sopenharmony_ci	/* It is not clean... Fix it to unload IPv6 module safely */
183762306a36Sopenharmony_ci	cancel_delayed_work_sync(&tbl->managed_work);
183862306a36Sopenharmony_ci	cancel_delayed_work_sync(&tbl->gc_work);
183962306a36Sopenharmony_ci	del_timer_sync(&tbl->proxy_timer);
184062306a36Sopenharmony_ci	pneigh_queue_purge(&tbl->proxy_queue, NULL, tbl->family);
184162306a36Sopenharmony_ci	neigh_ifdown(tbl, NULL);
184262306a36Sopenharmony_ci	if (atomic_read(&tbl->entries))
184362306a36Sopenharmony_ci		pr_crit("neighbour leakage\n");
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci	call_rcu(&rcu_dereference_protected(tbl->nht, 1)->rcu,
184662306a36Sopenharmony_ci		 neigh_hash_free_rcu);
184762306a36Sopenharmony_ci	tbl->nht = NULL;
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	kfree(tbl->phash_buckets);
185062306a36Sopenharmony_ci	tbl->phash_buckets = NULL;
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci	remove_proc_entry(tbl->id, init_net.proc_net_stat);
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci	free_percpu(tbl->stats);
185562306a36Sopenharmony_ci	tbl->stats = NULL;
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci	return 0;
185862306a36Sopenharmony_ci}
185962306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_table_clear);
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_cistatic struct neigh_table *neigh_find_table(int family)
186262306a36Sopenharmony_ci{
186362306a36Sopenharmony_ci	struct neigh_table *tbl = NULL;
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	switch (family) {
186662306a36Sopenharmony_ci	case AF_INET:
186762306a36Sopenharmony_ci		tbl = neigh_tables[NEIGH_ARP_TABLE];
186862306a36Sopenharmony_ci		break;
186962306a36Sopenharmony_ci	case AF_INET6:
187062306a36Sopenharmony_ci		tbl = neigh_tables[NEIGH_ND_TABLE];
187162306a36Sopenharmony_ci		break;
187262306a36Sopenharmony_ci	}
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci	return tbl;
187562306a36Sopenharmony_ci}
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_ciconst struct nla_policy nda_policy[NDA_MAX+1] = {
187862306a36Sopenharmony_ci	[NDA_UNSPEC]		= { .strict_start_type = NDA_NH_ID },
187962306a36Sopenharmony_ci	[NDA_DST]		= { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
188062306a36Sopenharmony_ci	[NDA_LLADDR]		= { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
188162306a36Sopenharmony_ci	[NDA_CACHEINFO]		= { .len = sizeof(struct nda_cacheinfo) },
188262306a36Sopenharmony_ci	[NDA_PROBES]		= { .type = NLA_U32 },
188362306a36Sopenharmony_ci	[NDA_VLAN]		= { .type = NLA_U16 },
188462306a36Sopenharmony_ci	[NDA_PORT]		= { .type = NLA_U16 },
188562306a36Sopenharmony_ci	[NDA_VNI]		= { .type = NLA_U32 },
188662306a36Sopenharmony_ci	[NDA_IFINDEX]		= { .type = NLA_U32 },
188762306a36Sopenharmony_ci	[NDA_MASTER]		= { .type = NLA_U32 },
188862306a36Sopenharmony_ci	[NDA_PROTOCOL]		= { .type = NLA_U8 },
188962306a36Sopenharmony_ci	[NDA_NH_ID]		= { .type = NLA_U32 },
189062306a36Sopenharmony_ci	[NDA_FLAGS_EXT]		= NLA_POLICY_MASK(NLA_U32, NTF_EXT_MASK),
189162306a36Sopenharmony_ci	[NDA_FDB_EXT_ATTRS]	= { .type = NLA_NESTED },
189262306a36Sopenharmony_ci};
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_cistatic int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh,
189562306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
189662306a36Sopenharmony_ci{
189762306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
189862306a36Sopenharmony_ci	struct ndmsg *ndm;
189962306a36Sopenharmony_ci	struct nlattr *dst_attr;
190062306a36Sopenharmony_ci	struct neigh_table *tbl;
190162306a36Sopenharmony_ci	struct neighbour *neigh;
190262306a36Sopenharmony_ci	struct net_device *dev = NULL;
190362306a36Sopenharmony_ci	int err = -EINVAL;
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci	ASSERT_RTNL();
190662306a36Sopenharmony_ci	if (nlmsg_len(nlh) < sizeof(*ndm))
190762306a36Sopenharmony_ci		goto out;
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci	dst_attr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_DST);
191062306a36Sopenharmony_ci	if (!dst_attr) {
191162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Network address not specified");
191262306a36Sopenharmony_ci		goto out;
191362306a36Sopenharmony_ci	}
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	ndm = nlmsg_data(nlh);
191662306a36Sopenharmony_ci	if (ndm->ndm_ifindex) {
191762306a36Sopenharmony_ci		dev = __dev_get_by_index(net, ndm->ndm_ifindex);
191862306a36Sopenharmony_ci		if (dev == NULL) {
191962306a36Sopenharmony_ci			err = -ENODEV;
192062306a36Sopenharmony_ci			goto out;
192162306a36Sopenharmony_ci		}
192262306a36Sopenharmony_ci	}
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci	tbl = neigh_find_table(ndm->ndm_family);
192562306a36Sopenharmony_ci	if (tbl == NULL)
192662306a36Sopenharmony_ci		return -EAFNOSUPPORT;
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	if (nla_len(dst_attr) < (int)tbl->key_len) {
192962306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid network address");
193062306a36Sopenharmony_ci		goto out;
193162306a36Sopenharmony_ci	}
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	if (ndm->ndm_flags & NTF_PROXY) {
193462306a36Sopenharmony_ci		err = pneigh_delete(tbl, net, nla_data(dst_attr), dev);
193562306a36Sopenharmony_ci		goto out;
193662306a36Sopenharmony_ci	}
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci	if (dev == NULL)
193962306a36Sopenharmony_ci		goto out;
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	neigh = neigh_lookup(tbl, nla_data(dst_attr), dev);
194262306a36Sopenharmony_ci	if (neigh == NULL) {
194362306a36Sopenharmony_ci		err = -ENOENT;
194462306a36Sopenharmony_ci		goto out;
194562306a36Sopenharmony_ci	}
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	err = __neigh_update(neigh, NULL, NUD_FAILED,
194862306a36Sopenharmony_ci			     NEIGH_UPDATE_F_OVERRIDE | NEIGH_UPDATE_F_ADMIN,
194962306a36Sopenharmony_ci			     NETLINK_CB(skb).portid, extack);
195062306a36Sopenharmony_ci	write_lock_bh(&tbl->lock);
195162306a36Sopenharmony_ci	neigh_release(neigh);
195262306a36Sopenharmony_ci	neigh_remove_one(neigh, tbl);
195362306a36Sopenharmony_ci	write_unlock_bh(&tbl->lock);
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ciout:
195662306a36Sopenharmony_ci	return err;
195762306a36Sopenharmony_ci}
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_cistatic int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh,
196062306a36Sopenharmony_ci		     struct netlink_ext_ack *extack)
196162306a36Sopenharmony_ci{
196262306a36Sopenharmony_ci	int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE |
196362306a36Sopenharmony_ci		    NEIGH_UPDATE_F_OVERRIDE_ISROUTER;
196462306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
196562306a36Sopenharmony_ci	struct ndmsg *ndm;
196662306a36Sopenharmony_ci	struct nlattr *tb[NDA_MAX+1];
196762306a36Sopenharmony_ci	struct neigh_table *tbl;
196862306a36Sopenharmony_ci	struct net_device *dev = NULL;
196962306a36Sopenharmony_ci	struct neighbour *neigh;
197062306a36Sopenharmony_ci	void *dst, *lladdr;
197162306a36Sopenharmony_ci	u8 protocol = 0;
197262306a36Sopenharmony_ci	u32 ndm_flags;
197362306a36Sopenharmony_ci	int err;
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci	ASSERT_RTNL();
197662306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(*ndm), tb, NDA_MAX,
197762306a36Sopenharmony_ci				     nda_policy, extack);
197862306a36Sopenharmony_ci	if (err < 0)
197962306a36Sopenharmony_ci		goto out;
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	err = -EINVAL;
198262306a36Sopenharmony_ci	if (!tb[NDA_DST]) {
198362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Network address not specified");
198462306a36Sopenharmony_ci		goto out;
198562306a36Sopenharmony_ci	}
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci	ndm = nlmsg_data(nlh);
198862306a36Sopenharmony_ci	ndm_flags = ndm->ndm_flags;
198962306a36Sopenharmony_ci	if (tb[NDA_FLAGS_EXT]) {
199062306a36Sopenharmony_ci		u32 ext = nla_get_u32(tb[NDA_FLAGS_EXT]);
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci		BUILD_BUG_ON(sizeof(neigh->flags) * BITS_PER_BYTE <
199362306a36Sopenharmony_ci			     (sizeof(ndm->ndm_flags) * BITS_PER_BYTE +
199462306a36Sopenharmony_ci			      hweight32(NTF_EXT_MASK)));
199562306a36Sopenharmony_ci		ndm_flags |= (ext << NTF_EXT_SHIFT);
199662306a36Sopenharmony_ci	}
199762306a36Sopenharmony_ci	if (ndm->ndm_ifindex) {
199862306a36Sopenharmony_ci		dev = __dev_get_by_index(net, ndm->ndm_ifindex);
199962306a36Sopenharmony_ci		if (dev == NULL) {
200062306a36Sopenharmony_ci			err = -ENODEV;
200162306a36Sopenharmony_ci			goto out;
200262306a36Sopenharmony_ci		}
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci		if (tb[NDA_LLADDR] && nla_len(tb[NDA_LLADDR]) < dev->addr_len) {
200562306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid link address");
200662306a36Sopenharmony_ci			goto out;
200762306a36Sopenharmony_ci		}
200862306a36Sopenharmony_ci	}
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci	tbl = neigh_find_table(ndm->ndm_family);
201162306a36Sopenharmony_ci	if (tbl == NULL)
201262306a36Sopenharmony_ci		return -EAFNOSUPPORT;
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	if (nla_len(tb[NDA_DST]) < (int)tbl->key_len) {
201562306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid network address");
201662306a36Sopenharmony_ci		goto out;
201762306a36Sopenharmony_ci	}
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci	dst = nla_data(tb[NDA_DST]);
202062306a36Sopenharmony_ci	lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL;
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	if (tb[NDA_PROTOCOL])
202362306a36Sopenharmony_ci		protocol = nla_get_u8(tb[NDA_PROTOCOL]);
202462306a36Sopenharmony_ci	if (ndm_flags & NTF_PROXY) {
202562306a36Sopenharmony_ci		struct pneigh_entry *pn;
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci		if (ndm_flags & NTF_MANAGED) {
202862306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid NTF_* flag combination");
202962306a36Sopenharmony_ci			goto out;
203062306a36Sopenharmony_ci		}
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci		err = -ENOBUFS;
203362306a36Sopenharmony_ci		pn = pneigh_lookup(tbl, net, dst, dev, 1);
203462306a36Sopenharmony_ci		if (pn) {
203562306a36Sopenharmony_ci			pn->flags = ndm_flags;
203662306a36Sopenharmony_ci			if (protocol)
203762306a36Sopenharmony_ci				pn->protocol = protocol;
203862306a36Sopenharmony_ci			err = 0;
203962306a36Sopenharmony_ci		}
204062306a36Sopenharmony_ci		goto out;
204162306a36Sopenharmony_ci	}
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ci	if (!dev) {
204462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Device not specified");
204562306a36Sopenharmony_ci		goto out;
204662306a36Sopenharmony_ci	}
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	if (tbl->allow_add && !tbl->allow_add(dev, extack)) {
204962306a36Sopenharmony_ci		err = -EINVAL;
205062306a36Sopenharmony_ci		goto out;
205162306a36Sopenharmony_ci	}
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci	neigh = neigh_lookup(tbl, dst, dev);
205462306a36Sopenharmony_ci	if (neigh == NULL) {
205562306a36Sopenharmony_ci		bool ndm_permanent  = ndm->ndm_state & NUD_PERMANENT;
205662306a36Sopenharmony_ci		bool exempt_from_gc = ndm_permanent ||
205762306a36Sopenharmony_ci				      ndm_flags & NTF_EXT_LEARNED;
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci		if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
206062306a36Sopenharmony_ci			err = -ENOENT;
206162306a36Sopenharmony_ci			goto out;
206262306a36Sopenharmony_ci		}
206362306a36Sopenharmony_ci		if (ndm_permanent && (ndm_flags & NTF_MANAGED)) {
206462306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid NTF_* flag for permanent entry");
206562306a36Sopenharmony_ci			err = -EINVAL;
206662306a36Sopenharmony_ci			goto out;
206762306a36Sopenharmony_ci		}
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci		neigh = ___neigh_create(tbl, dst, dev,
207062306a36Sopenharmony_ci					ndm_flags &
207162306a36Sopenharmony_ci					(NTF_EXT_LEARNED | NTF_MANAGED),
207262306a36Sopenharmony_ci					exempt_from_gc, true);
207362306a36Sopenharmony_ci		if (IS_ERR(neigh)) {
207462306a36Sopenharmony_ci			err = PTR_ERR(neigh);
207562306a36Sopenharmony_ci			goto out;
207662306a36Sopenharmony_ci		}
207762306a36Sopenharmony_ci	} else {
207862306a36Sopenharmony_ci		if (nlh->nlmsg_flags & NLM_F_EXCL) {
207962306a36Sopenharmony_ci			err = -EEXIST;
208062306a36Sopenharmony_ci			neigh_release(neigh);
208162306a36Sopenharmony_ci			goto out;
208262306a36Sopenharmony_ci		}
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci		if (!(nlh->nlmsg_flags & NLM_F_REPLACE))
208562306a36Sopenharmony_ci			flags &= ~(NEIGH_UPDATE_F_OVERRIDE |
208662306a36Sopenharmony_ci				   NEIGH_UPDATE_F_OVERRIDE_ISROUTER);
208762306a36Sopenharmony_ci	}
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci	if (protocol)
209062306a36Sopenharmony_ci		neigh->protocol = protocol;
209162306a36Sopenharmony_ci	if (ndm_flags & NTF_EXT_LEARNED)
209262306a36Sopenharmony_ci		flags |= NEIGH_UPDATE_F_EXT_LEARNED;
209362306a36Sopenharmony_ci	if (ndm_flags & NTF_ROUTER)
209462306a36Sopenharmony_ci		flags |= NEIGH_UPDATE_F_ISROUTER;
209562306a36Sopenharmony_ci	if (ndm_flags & NTF_MANAGED)
209662306a36Sopenharmony_ci		flags |= NEIGH_UPDATE_F_MANAGED;
209762306a36Sopenharmony_ci	if (ndm_flags & NTF_USE)
209862306a36Sopenharmony_ci		flags |= NEIGH_UPDATE_F_USE;
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci	err = __neigh_update(neigh, lladdr, ndm->ndm_state, flags,
210162306a36Sopenharmony_ci			     NETLINK_CB(skb).portid, extack);
210262306a36Sopenharmony_ci	if (!err && ndm_flags & (NTF_USE | NTF_MANAGED)) {
210362306a36Sopenharmony_ci		neigh_event_send(neigh, NULL);
210462306a36Sopenharmony_ci		err = 0;
210562306a36Sopenharmony_ci	}
210662306a36Sopenharmony_ci	neigh_release(neigh);
210762306a36Sopenharmony_ciout:
210862306a36Sopenharmony_ci	return err;
210962306a36Sopenharmony_ci}
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_cistatic int neightbl_fill_parms(struct sk_buff *skb, struct neigh_parms *parms)
211262306a36Sopenharmony_ci{
211362306a36Sopenharmony_ci	struct nlattr *nest;
211462306a36Sopenharmony_ci
211562306a36Sopenharmony_ci	nest = nla_nest_start_noflag(skb, NDTA_PARMS);
211662306a36Sopenharmony_ci	if (nest == NULL)
211762306a36Sopenharmony_ci		return -ENOBUFS;
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci	if ((parms->dev &&
212062306a36Sopenharmony_ci	     nla_put_u32(skb, NDTPA_IFINDEX, parms->dev->ifindex)) ||
212162306a36Sopenharmony_ci	    nla_put_u32(skb, NDTPA_REFCNT, refcount_read(&parms->refcnt)) ||
212262306a36Sopenharmony_ci	    nla_put_u32(skb, NDTPA_QUEUE_LENBYTES,
212362306a36Sopenharmony_ci			NEIGH_VAR(parms, QUEUE_LEN_BYTES)) ||
212462306a36Sopenharmony_ci	    /* approximative value for deprecated QUEUE_LEN (in packets) */
212562306a36Sopenharmony_ci	    nla_put_u32(skb, NDTPA_QUEUE_LEN,
212662306a36Sopenharmony_ci			NEIGH_VAR(parms, QUEUE_LEN_BYTES) / SKB_TRUESIZE(ETH_FRAME_LEN)) ||
212762306a36Sopenharmony_ci	    nla_put_u32(skb, NDTPA_PROXY_QLEN, NEIGH_VAR(parms, PROXY_QLEN)) ||
212862306a36Sopenharmony_ci	    nla_put_u32(skb, NDTPA_APP_PROBES, NEIGH_VAR(parms, APP_PROBES)) ||
212962306a36Sopenharmony_ci	    nla_put_u32(skb, NDTPA_UCAST_PROBES,
213062306a36Sopenharmony_ci			NEIGH_VAR(parms, UCAST_PROBES)) ||
213162306a36Sopenharmony_ci	    nla_put_u32(skb, NDTPA_MCAST_PROBES,
213262306a36Sopenharmony_ci			NEIGH_VAR(parms, MCAST_PROBES)) ||
213362306a36Sopenharmony_ci	    nla_put_u32(skb, NDTPA_MCAST_REPROBES,
213462306a36Sopenharmony_ci			NEIGH_VAR(parms, MCAST_REPROBES)) ||
213562306a36Sopenharmony_ci	    nla_put_msecs(skb, NDTPA_REACHABLE_TIME, parms->reachable_time,
213662306a36Sopenharmony_ci			  NDTPA_PAD) ||
213762306a36Sopenharmony_ci	    nla_put_msecs(skb, NDTPA_BASE_REACHABLE_TIME,
213862306a36Sopenharmony_ci			  NEIGH_VAR(parms, BASE_REACHABLE_TIME), NDTPA_PAD) ||
213962306a36Sopenharmony_ci	    nla_put_msecs(skb, NDTPA_GC_STALETIME,
214062306a36Sopenharmony_ci			  NEIGH_VAR(parms, GC_STALETIME), NDTPA_PAD) ||
214162306a36Sopenharmony_ci	    nla_put_msecs(skb, NDTPA_DELAY_PROBE_TIME,
214262306a36Sopenharmony_ci			  NEIGH_VAR(parms, DELAY_PROBE_TIME), NDTPA_PAD) ||
214362306a36Sopenharmony_ci	    nla_put_msecs(skb, NDTPA_RETRANS_TIME,
214462306a36Sopenharmony_ci			  NEIGH_VAR(parms, RETRANS_TIME), NDTPA_PAD) ||
214562306a36Sopenharmony_ci	    nla_put_msecs(skb, NDTPA_ANYCAST_DELAY,
214662306a36Sopenharmony_ci			  NEIGH_VAR(parms, ANYCAST_DELAY), NDTPA_PAD) ||
214762306a36Sopenharmony_ci	    nla_put_msecs(skb, NDTPA_PROXY_DELAY,
214862306a36Sopenharmony_ci			  NEIGH_VAR(parms, PROXY_DELAY), NDTPA_PAD) ||
214962306a36Sopenharmony_ci	    nla_put_msecs(skb, NDTPA_LOCKTIME,
215062306a36Sopenharmony_ci			  NEIGH_VAR(parms, LOCKTIME), NDTPA_PAD) ||
215162306a36Sopenharmony_ci	    nla_put_msecs(skb, NDTPA_INTERVAL_PROBE_TIME_MS,
215262306a36Sopenharmony_ci			  NEIGH_VAR(parms, INTERVAL_PROBE_TIME_MS), NDTPA_PAD))
215362306a36Sopenharmony_ci		goto nla_put_failure;
215462306a36Sopenharmony_ci	return nla_nest_end(skb, nest);
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_cinla_put_failure:
215762306a36Sopenharmony_ci	nla_nest_cancel(skb, nest);
215862306a36Sopenharmony_ci	return -EMSGSIZE;
215962306a36Sopenharmony_ci}
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_cistatic int neightbl_fill_info(struct sk_buff *skb, struct neigh_table *tbl,
216262306a36Sopenharmony_ci			      u32 pid, u32 seq, int type, int flags)
216362306a36Sopenharmony_ci{
216462306a36Sopenharmony_ci	struct nlmsghdr *nlh;
216562306a36Sopenharmony_ci	struct ndtmsg *ndtmsg;
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndtmsg), flags);
216862306a36Sopenharmony_ci	if (nlh == NULL)
216962306a36Sopenharmony_ci		return -EMSGSIZE;
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci	ndtmsg = nlmsg_data(nlh);
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci	read_lock_bh(&tbl->lock);
217462306a36Sopenharmony_ci	ndtmsg->ndtm_family = tbl->family;
217562306a36Sopenharmony_ci	ndtmsg->ndtm_pad1   = 0;
217662306a36Sopenharmony_ci	ndtmsg->ndtm_pad2   = 0;
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci	if (nla_put_string(skb, NDTA_NAME, tbl->id) ||
217962306a36Sopenharmony_ci	    nla_put_msecs(skb, NDTA_GC_INTERVAL, READ_ONCE(tbl->gc_interval),
218062306a36Sopenharmony_ci			  NDTA_PAD) ||
218162306a36Sopenharmony_ci	    nla_put_u32(skb, NDTA_THRESH1, READ_ONCE(tbl->gc_thresh1)) ||
218262306a36Sopenharmony_ci	    nla_put_u32(skb, NDTA_THRESH2, READ_ONCE(tbl->gc_thresh2)) ||
218362306a36Sopenharmony_ci	    nla_put_u32(skb, NDTA_THRESH3, READ_ONCE(tbl->gc_thresh3)))
218462306a36Sopenharmony_ci		goto nla_put_failure;
218562306a36Sopenharmony_ci	{
218662306a36Sopenharmony_ci		unsigned long now = jiffies;
218762306a36Sopenharmony_ci		long flush_delta = now - READ_ONCE(tbl->last_flush);
218862306a36Sopenharmony_ci		long rand_delta = now - READ_ONCE(tbl->last_rand);
218962306a36Sopenharmony_ci		struct neigh_hash_table *nht;
219062306a36Sopenharmony_ci		struct ndt_config ndc = {
219162306a36Sopenharmony_ci			.ndtc_key_len		= tbl->key_len,
219262306a36Sopenharmony_ci			.ndtc_entry_size	= tbl->entry_size,
219362306a36Sopenharmony_ci			.ndtc_entries		= atomic_read(&tbl->entries),
219462306a36Sopenharmony_ci			.ndtc_last_flush	= jiffies_to_msecs(flush_delta),
219562306a36Sopenharmony_ci			.ndtc_last_rand		= jiffies_to_msecs(rand_delta),
219662306a36Sopenharmony_ci			.ndtc_proxy_qlen	= READ_ONCE(tbl->proxy_queue.qlen),
219762306a36Sopenharmony_ci		};
219862306a36Sopenharmony_ci
219962306a36Sopenharmony_ci		rcu_read_lock();
220062306a36Sopenharmony_ci		nht = rcu_dereference(tbl->nht);
220162306a36Sopenharmony_ci		ndc.ndtc_hash_rnd = nht->hash_rnd[0];
220262306a36Sopenharmony_ci		ndc.ndtc_hash_mask = ((1 << nht->hash_shift) - 1);
220362306a36Sopenharmony_ci		rcu_read_unlock();
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci		if (nla_put(skb, NDTA_CONFIG, sizeof(ndc), &ndc))
220662306a36Sopenharmony_ci			goto nla_put_failure;
220762306a36Sopenharmony_ci	}
220862306a36Sopenharmony_ci
220962306a36Sopenharmony_ci	{
221062306a36Sopenharmony_ci		int cpu;
221162306a36Sopenharmony_ci		struct ndt_stats ndst;
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci		memset(&ndst, 0, sizeof(ndst));
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_ci		for_each_possible_cpu(cpu) {
221662306a36Sopenharmony_ci			struct neigh_statistics	*st;
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci			st = per_cpu_ptr(tbl->stats, cpu);
221962306a36Sopenharmony_ci			ndst.ndts_allocs		+= READ_ONCE(st->allocs);
222062306a36Sopenharmony_ci			ndst.ndts_destroys		+= READ_ONCE(st->destroys);
222162306a36Sopenharmony_ci			ndst.ndts_hash_grows		+= READ_ONCE(st->hash_grows);
222262306a36Sopenharmony_ci			ndst.ndts_res_failed		+= READ_ONCE(st->res_failed);
222362306a36Sopenharmony_ci			ndst.ndts_lookups		+= READ_ONCE(st->lookups);
222462306a36Sopenharmony_ci			ndst.ndts_hits			+= READ_ONCE(st->hits);
222562306a36Sopenharmony_ci			ndst.ndts_rcv_probes_mcast	+= READ_ONCE(st->rcv_probes_mcast);
222662306a36Sopenharmony_ci			ndst.ndts_rcv_probes_ucast	+= READ_ONCE(st->rcv_probes_ucast);
222762306a36Sopenharmony_ci			ndst.ndts_periodic_gc_runs	+= READ_ONCE(st->periodic_gc_runs);
222862306a36Sopenharmony_ci			ndst.ndts_forced_gc_runs	+= READ_ONCE(st->forced_gc_runs);
222962306a36Sopenharmony_ci			ndst.ndts_table_fulls		+= READ_ONCE(st->table_fulls);
223062306a36Sopenharmony_ci		}
223162306a36Sopenharmony_ci
223262306a36Sopenharmony_ci		if (nla_put_64bit(skb, NDTA_STATS, sizeof(ndst), &ndst,
223362306a36Sopenharmony_ci				  NDTA_PAD))
223462306a36Sopenharmony_ci			goto nla_put_failure;
223562306a36Sopenharmony_ci	}
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci	BUG_ON(tbl->parms.dev);
223862306a36Sopenharmony_ci	if (neightbl_fill_parms(skb, &tbl->parms) < 0)
223962306a36Sopenharmony_ci		goto nla_put_failure;
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_ci	read_unlock_bh(&tbl->lock);
224262306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
224362306a36Sopenharmony_ci	return 0;
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_cinla_put_failure:
224662306a36Sopenharmony_ci	read_unlock_bh(&tbl->lock);
224762306a36Sopenharmony_ci	nlmsg_cancel(skb, nlh);
224862306a36Sopenharmony_ci	return -EMSGSIZE;
224962306a36Sopenharmony_ci}
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_cistatic int neightbl_fill_param_info(struct sk_buff *skb,
225262306a36Sopenharmony_ci				    struct neigh_table *tbl,
225362306a36Sopenharmony_ci				    struct neigh_parms *parms,
225462306a36Sopenharmony_ci				    u32 pid, u32 seq, int type,
225562306a36Sopenharmony_ci				    unsigned int flags)
225662306a36Sopenharmony_ci{
225762306a36Sopenharmony_ci	struct ndtmsg *ndtmsg;
225862306a36Sopenharmony_ci	struct nlmsghdr *nlh;
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_ci	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndtmsg), flags);
226162306a36Sopenharmony_ci	if (nlh == NULL)
226262306a36Sopenharmony_ci		return -EMSGSIZE;
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_ci	ndtmsg = nlmsg_data(nlh);
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	read_lock_bh(&tbl->lock);
226762306a36Sopenharmony_ci	ndtmsg->ndtm_family = tbl->family;
226862306a36Sopenharmony_ci	ndtmsg->ndtm_pad1   = 0;
226962306a36Sopenharmony_ci	ndtmsg->ndtm_pad2   = 0;
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_ci	if (nla_put_string(skb, NDTA_NAME, tbl->id) < 0 ||
227262306a36Sopenharmony_ci	    neightbl_fill_parms(skb, parms) < 0)
227362306a36Sopenharmony_ci		goto errout;
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_ci	read_unlock_bh(&tbl->lock);
227662306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
227762306a36Sopenharmony_ci	return 0;
227862306a36Sopenharmony_cierrout:
227962306a36Sopenharmony_ci	read_unlock_bh(&tbl->lock);
228062306a36Sopenharmony_ci	nlmsg_cancel(skb, nlh);
228162306a36Sopenharmony_ci	return -EMSGSIZE;
228262306a36Sopenharmony_ci}
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_cistatic const struct nla_policy nl_neightbl_policy[NDTA_MAX+1] = {
228562306a36Sopenharmony_ci	[NDTA_NAME]		= { .type = NLA_STRING },
228662306a36Sopenharmony_ci	[NDTA_THRESH1]		= { .type = NLA_U32 },
228762306a36Sopenharmony_ci	[NDTA_THRESH2]		= { .type = NLA_U32 },
228862306a36Sopenharmony_ci	[NDTA_THRESH3]		= { .type = NLA_U32 },
228962306a36Sopenharmony_ci	[NDTA_GC_INTERVAL]	= { .type = NLA_U64 },
229062306a36Sopenharmony_ci	[NDTA_PARMS]		= { .type = NLA_NESTED },
229162306a36Sopenharmony_ci};
229262306a36Sopenharmony_ci
229362306a36Sopenharmony_cistatic const struct nla_policy nl_ntbl_parm_policy[NDTPA_MAX+1] = {
229462306a36Sopenharmony_ci	[NDTPA_IFINDEX]			= { .type = NLA_U32 },
229562306a36Sopenharmony_ci	[NDTPA_QUEUE_LEN]		= { .type = NLA_U32 },
229662306a36Sopenharmony_ci	[NDTPA_PROXY_QLEN]		= { .type = NLA_U32 },
229762306a36Sopenharmony_ci	[NDTPA_APP_PROBES]		= { .type = NLA_U32 },
229862306a36Sopenharmony_ci	[NDTPA_UCAST_PROBES]		= { .type = NLA_U32 },
229962306a36Sopenharmony_ci	[NDTPA_MCAST_PROBES]		= { .type = NLA_U32 },
230062306a36Sopenharmony_ci	[NDTPA_MCAST_REPROBES]		= { .type = NLA_U32 },
230162306a36Sopenharmony_ci	[NDTPA_BASE_REACHABLE_TIME]	= { .type = NLA_U64 },
230262306a36Sopenharmony_ci	[NDTPA_GC_STALETIME]		= { .type = NLA_U64 },
230362306a36Sopenharmony_ci	[NDTPA_DELAY_PROBE_TIME]	= { .type = NLA_U64 },
230462306a36Sopenharmony_ci	[NDTPA_RETRANS_TIME]		= { .type = NLA_U64 },
230562306a36Sopenharmony_ci	[NDTPA_ANYCAST_DELAY]		= { .type = NLA_U64 },
230662306a36Sopenharmony_ci	[NDTPA_PROXY_DELAY]		= { .type = NLA_U64 },
230762306a36Sopenharmony_ci	[NDTPA_LOCKTIME]		= { .type = NLA_U64 },
230862306a36Sopenharmony_ci	[NDTPA_INTERVAL_PROBE_TIME_MS]	= { .type = NLA_U64, .min = 1 },
230962306a36Sopenharmony_ci};
231062306a36Sopenharmony_ci
231162306a36Sopenharmony_cistatic int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh,
231262306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
231362306a36Sopenharmony_ci{
231462306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
231562306a36Sopenharmony_ci	struct neigh_table *tbl;
231662306a36Sopenharmony_ci	struct ndtmsg *ndtmsg;
231762306a36Sopenharmony_ci	struct nlattr *tb[NDTA_MAX+1];
231862306a36Sopenharmony_ci	bool found = false;
231962306a36Sopenharmony_ci	int err, tidx;
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	err = nlmsg_parse_deprecated(nlh, sizeof(*ndtmsg), tb, NDTA_MAX,
232262306a36Sopenharmony_ci				     nl_neightbl_policy, extack);
232362306a36Sopenharmony_ci	if (err < 0)
232462306a36Sopenharmony_ci		goto errout;
232562306a36Sopenharmony_ci
232662306a36Sopenharmony_ci	if (tb[NDTA_NAME] == NULL) {
232762306a36Sopenharmony_ci		err = -EINVAL;
232862306a36Sopenharmony_ci		goto errout;
232962306a36Sopenharmony_ci	}
233062306a36Sopenharmony_ci
233162306a36Sopenharmony_ci	ndtmsg = nlmsg_data(nlh);
233262306a36Sopenharmony_ci
233362306a36Sopenharmony_ci	for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) {
233462306a36Sopenharmony_ci		tbl = neigh_tables[tidx];
233562306a36Sopenharmony_ci		if (!tbl)
233662306a36Sopenharmony_ci			continue;
233762306a36Sopenharmony_ci		if (ndtmsg->ndtm_family && tbl->family != ndtmsg->ndtm_family)
233862306a36Sopenharmony_ci			continue;
233962306a36Sopenharmony_ci		if (nla_strcmp(tb[NDTA_NAME], tbl->id) == 0) {
234062306a36Sopenharmony_ci			found = true;
234162306a36Sopenharmony_ci			break;
234262306a36Sopenharmony_ci		}
234362306a36Sopenharmony_ci	}
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ci	if (!found)
234662306a36Sopenharmony_ci		return -ENOENT;
234762306a36Sopenharmony_ci
234862306a36Sopenharmony_ci	/*
234962306a36Sopenharmony_ci	 * We acquire tbl->lock to be nice to the periodic timers and
235062306a36Sopenharmony_ci	 * make sure they always see a consistent set of values.
235162306a36Sopenharmony_ci	 */
235262306a36Sopenharmony_ci	write_lock_bh(&tbl->lock);
235362306a36Sopenharmony_ci
235462306a36Sopenharmony_ci	if (tb[NDTA_PARMS]) {
235562306a36Sopenharmony_ci		struct nlattr *tbp[NDTPA_MAX+1];
235662306a36Sopenharmony_ci		struct neigh_parms *p;
235762306a36Sopenharmony_ci		int i, ifindex = 0;
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci		err = nla_parse_nested_deprecated(tbp, NDTPA_MAX,
236062306a36Sopenharmony_ci						  tb[NDTA_PARMS],
236162306a36Sopenharmony_ci						  nl_ntbl_parm_policy, extack);
236262306a36Sopenharmony_ci		if (err < 0)
236362306a36Sopenharmony_ci			goto errout_tbl_lock;
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci		if (tbp[NDTPA_IFINDEX])
236662306a36Sopenharmony_ci			ifindex = nla_get_u32(tbp[NDTPA_IFINDEX]);
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_ci		p = lookup_neigh_parms(tbl, net, ifindex);
236962306a36Sopenharmony_ci		if (p == NULL) {
237062306a36Sopenharmony_ci			err = -ENOENT;
237162306a36Sopenharmony_ci			goto errout_tbl_lock;
237262306a36Sopenharmony_ci		}
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_ci		for (i = 1; i <= NDTPA_MAX; i++) {
237562306a36Sopenharmony_ci			if (tbp[i] == NULL)
237662306a36Sopenharmony_ci				continue;
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_ci			switch (i) {
237962306a36Sopenharmony_ci			case NDTPA_QUEUE_LEN:
238062306a36Sopenharmony_ci				NEIGH_VAR_SET(p, QUEUE_LEN_BYTES,
238162306a36Sopenharmony_ci					      nla_get_u32(tbp[i]) *
238262306a36Sopenharmony_ci					      SKB_TRUESIZE(ETH_FRAME_LEN));
238362306a36Sopenharmony_ci				break;
238462306a36Sopenharmony_ci			case NDTPA_QUEUE_LENBYTES:
238562306a36Sopenharmony_ci				NEIGH_VAR_SET(p, QUEUE_LEN_BYTES,
238662306a36Sopenharmony_ci					      nla_get_u32(tbp[i]));
238762306a36Sopenharmony_ci				break;
238862306a36Sopenharmony_ci			case NDTPA_PROXY_QLEN:
238962306a36Sopenharmony_ci				NEIGH_VAR_SET(p, PROXY_QLEN,
239062306a36Sopenharmony_ci					      nla_get_u32(tbp[i]));
239162306a36Sopenharmony_ci				break;
239262306a36Sopenharmony_ci			case NDTPA_APP_PROBES:
239362306a36Sopenharmony_ci				NEIGH_VAR_SET(p, APP_PROBES,
239462306a36Sopenharmony_ci					      nla_get_u32(tbp[i]));
239562306a36Sopenharmony_ci				break;
239662306a36Sopenharmony_ci			case NDTPA_UCAST_PROBES:
239762306a36Sopenharmony_ci				NEIGH_VAR_SET(p, UCAST_PROBES,
239862306a36Sopenharmony_ci					      nla_get_u32(tbp[i]));
239962306a36Sopenharmony_ci				break;
240062306a36Sopenharmony_ci			case NDTPA_MCAST_PROBES:
240162306a36Sopenharmony_ci				NEIGH_VAR_SET(p, MCAST_PROBES,
240262306a36Sopenharmony_ci					      nla_get_u32(tbp[i]));
240362306a36Sopenharmony_ci				break;
240462306a36Sopenharmony_ci			case NDTPA_MCAST_REPROBES:
240562306a36Sopenharmony_ci				NEIGH_VAR_SET(p, MCAST_REPROBES,
240662306a36Sopenharmony_ci					      nla_get_u32(tbp[i]));
240762306a36Sopenharmony_ci				break;
240862306a36Sopenharmony_ci			case NDTPA_BASE_REACHABLE_TIME:
240962306a36Sopenharmony_ci				NEIGH_VAR_SET(p, BASE_REACHABLE_TIME,
241062306a36Sopenharmony_ci					      nla_get_msecs(tbp[i]));
241162306a36Sopenharmony_ci				/* update reachable_time as well, otherwise, the change will
241262306a36Sopenharmony_ci				 * only be effective after the next time neigh_periodic_work
241362306a36Sopenharmony_ci				 * decides to recompute it (can be multiple minutes)
241462306a36Sopenharmony_ci				 */
241562306a36Sopenharmony_ci				p->reachable_time =
241662306a36Sopenharmony_ci					neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME));
241762306a36Sopenharmony_ci				break;
241862306a36Sopenharmony_ci			case NDTPA_GC_STALETIME:
241962306a36Sopenharmony_ci				NEIGH_VAR_SET(p, GC_STALETIME,
242062306a36Sopenharmony_ci					      nla_get_msecs(tbp[i]));
242162306a36Sopenharmony_ci				break;
242262306a36Sopenharmony_ci			case NDTPA_DELAY_PROBE_TIME:
242362306a36Sopenharmony_ci				NEIGH_VAR_SET(p, DELAY_PROBE_TIME,
242462306a36Sopenharmony_ci					      nla_get_msecs(tbp[i]));
242562306a36Sopenharmony_ci				call_netevent_notifiers(NETEVENT_DELAY_PROBE_TIME_UPDATE, p);
242662306a36Sopenharmony_ci				break;
242762306a36Sopenharmony_ci			case NDTPA_INTERVAL_PROBE_TIME_MS:
242862306a36Sopenharmony_ci				NEIGH_VAR_SET(p, INTERVAL_PROBE_TIME_MS,
242962306a36Sopenharmony_ci					      nla_get_msecs(tbp[i]));
243062306a36Sopenharmony_ci				break;
243162306a36Sopenharmony_ci			case NDTPA_RETRANS_TIME:
243262306a36Sopenharmony_ci				NEIGH_VAR_SET(p, RETRANS_TIME,
243362306a36Sopenharmony_ci					      nla_get_msecs(tbp[i]));
243462306a36Sopenharmony_ci				break;
243562306a36Sopenharmony_ci			case NDTPA_ANYCAST_DELAY:
243662306a36Sopenharmony_ci				NEIGH_VAR_SET(p, ANYCAST_DELAY,
243762306a36Sopenharmony_ci					      nla_get_msecs(tbp[i]));
243862306a36Sopenharmony_ci				break;
243962306a36Sopenharmony_ci			case NDTPA_PROXY_DELAY:
244062306a36Sopenharmony_ci				NEIGH_VAR_SET(p, PROXY_DELAY,
244162306a36Sopenharmony_ci					      nla_get_msecs(tbp[i]));
244262306a36Sopenharmony_ci				break;
244362306a36Sopenharmony_ci			case NDTPA_LOCKTIME:
244462306a36Sopenharmony_ci				NEIGH_VAR_SET(p, LOCKTIME,
244562306a36Sopenharmony_ci					      nla_get_msecs(tbp[i]));
244662306a36Sopenharmony_ci				break;
244762306a36Sopenharmony_ci			}
244862306a36Sopenharmony_ci		}
244962306a36Sopenharmony_ci	}
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci	err = -ENOENT;
245262306a36Sopenharmony_ci	if ((tb[NDTA_THRESH1] || tb[NDTA_THRESH2] ||
245362306a36Sopenharmony_ci	     tb[NDTA_THRESH3] || tb[NDTA_GC_INTERVAL]) &&
245462306a36Sopenharmony_ci	    !net_eq(net, &init_net))
245562306a36Sopenharmony_ci		goto errout_tbl_lock;
245662306a36Sopenharmony_ci
245762306a36Sopenharmony_ci	if (tb[NDTA_THRESH1])
245862306a36Sopenharmony_ci		WRITE_ONCE(tbl->gc_thresh1, nla_get_u32(tb[NDTA_THRESH1]));
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci	if (tb[NDTA_THRESH2])
246162306a36Sopenharmony_ci		WRITE_ONCE(tbl->gc_thresh2, nla_get_u32(tb[NDTA_THRESH2]));
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci	if (tb[NDTA_THRESH3])
246462306a36Sopenharmony_ci		WRITE_ONCE(tbl->gc_thresh3, nla_get_u32(tb[NDTA_THRESH3]));
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci	if (tb[NDTA_GC_INTERVAL])
246762306a36Sopenharmony_ci		WRITE_ONCE(tbl->gc_interval, nla_get_msecs(tb[NDTA_GC_INTERVAL]));
246862306a36Sopenharmony_ci
246962306a36Sopenharmony_ci	err = 0;
247062306a36Sopenharmony_ci
247162306a36Sopenharmony_cierrout_tbl_lock:
247262306a36Sopenharmony_ci	write_unlock_bh(&tbl->lock);
247362306a36Sopenharmony_cierrout:
247462306a36Sopenharmony_ci	return err;
247562306a36Sopenharmony_ci}
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_cistatic int neightbl_valid_dump_info(const struct nlmsghdr *nlh,
247862306a36Sopenharmony_ci				    struct netlink_ext_ack *extack)
247962306a36Sopenharmony_ci{
248062306a36Sopenharmony_ci	struct ndtmsg *ndtm;
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndtm))) {
248362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid header for neighbor table dump request");
248462306a36Sopenharmony_ci		return -EINVAL;
248562306a36Sopenharmony_ci	}
248662306a36Sopenharmony_ci
248762306a36Sopenharmony_ci	ndtm = nlmsg_data(nlh);
248862306a36Sopenharmony_ci	if (ndtm->ndtm_pad1  || ndtm->ndtm_pad2) {
248962306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor table dump request");
249062306a36Sopenharmony_ci		return -EINVAL;
249162306a36Sopenharmony_ci	}
249262306a36Sopenharmony_ci
249362306a36Sopenharmony_ci	if (nlmsg_attrlen(nlh, sizeof(*ndtm))) {
249462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid data after header in neighbor table dump request");
249562306a36Sopenharmony_ci		return -EINVAL;
249662306a36Sopenharmony_ci	}
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci	return 0;
249962306a36Sopenharmony_ci}
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_cistatic int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
250262306a36Sopenharmony_ci{
250362306a36Sopenharmony_ci	const struct nlmsghdr *nlh = cb->nlh;
250462306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
250562306a36Sopenharmony_ci	int family, tidx, nidx = 0;
250662306a36Sopenharmony_ci	int tbl_skip = cb->args[0];
250762306a36Sopenharmony_ci	int neigh_skip = cb->args[1];
250862306a36Sopenharmony_ci	struct neigh_table *tbl;
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_ci	if (cb->strict_check) {
251162306a36Sopenharmony_ci		int err = neightbl_valid_dump_info(nlh, cb->extack);
251262306a36Sopenharmony_ci
251362306a36Sopenharmony_ci		if (err < 0)
251462306a36Sopenharmony_ci			return err;
251562306a36Sopenharmony_ci	}
251662306a36Sopenharmony_ci
251762306a36Sopenharmony_ci	family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family;
251862306a36Sopenharmony_ci
251962306a36Sopenharmony_ci	for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) {
252062306a36Sopenharmony_ci		struct neigh_parms *p;
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ci		tbl = neigh_tables[tidx];
252362306a36Sopenharmony_ci		if (!tbl)
252462306a36Sopenharmony_ci			continue;
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_ci		if (tidx < tbl_skip || (family && tbl->family != family))
252762306a36Sopenharmony_ci			continue;
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci		if (neightbl_fill_info(skb, tbl, NETLINK_CB(cb->skb).portid,
253062306a36Sopenharmony_ci				       nlh->nlmsg_seq, RTM_NEWNEIGHTBL,
253162306a36Sopenharmony_ci				       NLM_F_MULTI) < 0)
253262306a36Sopenharmony_ci			break;
253362306a36Sopenharmony_ci
253462306a36Sopenharmony_ci		nidx = 0;
253562306a36Sopenharmony_ci		p = list_next_entry(&tbl->parms, list);
253662306a36Sopenharmony_ci		list_for_each_entry_from(p, &tbl->parms_list, list) {
253762306a36Sopenharmony_ci			if (!net_eq(neigh_parms_net(p), net))
253862306a36Sopenharmony_ci				continue;
253962306a36Sopenharmony_ci
254062306a36Sopenharmony_ci			if (nidx < neigh_skip)
254162306a36Sopenharmony_ci				goto next;
254262306a36Sopenharmony_ci
254362306a36Sopenharmony_ci			if (neightbl_fill_param_info(skb, tbl, p,
254462306a36Sopenharmony_ci						     NETLINK_CB(cb->skb).portid,
254562306a36Sopenharmony_ci						     nlh->nlmsg_seq,
254662306a36Sopenharmony_ci						     RTM_NEWNEIGHTBL,
254762306a36Sopenharmony_ci						     NLM_F_MULTI) < 0)
254862306a36Sopenharmony_ci				goto out;
254962306a36Sopenharmony_ci		next:
255062306a36Sopenharmony_ci			nidx++;
255162306a36Sopenharmony_ci		}
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_ci		neigh_skip = 0;
255462306a36Sopenharmony_ci	}
255562306a36Sopenharmony_ciout:
255662306a36Sopenharmony_ci	cb->args[0] = tidx;
255762306a36Sopenharmony_ci	cb->args[1] = nidx;
255862306a36Sopenharmony_ci
255962306a36Sopenharmony_ci	return skb->len;
256062306a36Sopenharmony_ci}
256162306a36Sopenharmony_ci
256262306a36Sopenharmony_cistatic int neigh_fill_info(struct sk_buff *skb, struct neighbour *neigh,
256362306a36Sopenharmony_ci			   u32 pid, u32 seq, int type, unsigned int flags)
256462306a36Sopenharmony_ci{
256562306a36Sopenharmony_ci	u32 neigh_flags, neigh_flags_ext;
256662306a36Sopenharmony_ci	unsigned long now = jiffies;
256762306a36Sopenharmony_ci	struct nda_cacheinfo ci;
256862306a36Sopenharmony_ci	struct nlmsghdr *nlh;
256962306a36Sopenharmony_ci	struct ndmsg *ndm;
257062306a36Sopenharmony_ci
257162306a36Sopenharmony_ci	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags);
257262306a36Sopenharmony_ci	if (nlh == NULL)
257362306a36Sopenharmony_ci		return -EMSGSIZE;
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_ci	neigh_flags_ext = neigh->flags >> NTF_EXT_SHIFT;
257662306a36Sopenharmony_ci	neigh_flags     = neigh->flags & NTF_OLD_MASK;
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ci	ndm = nlmsg_data(nlh);
257962306a36Sopenharmony_ci	ndm->ndm_family	 = neigh->ops->family;
258062306a36Sopenharmony_ci	ndm->ndm_pad1    = 0;
258162306a36Sopenharmony_ci	ndm->ndm_pad2    = 0;
258262306a36Sopenharmony_ci	ndm->ndm_flags	 = neigh_flags;
258362306a36Sopenharmony_ci	ndm->ndm_type	 = neigh->type;
258462306a36Sopenharmony_ci	ndm->ndm_ifindex = neigh->dev->ifindex;
258562306a36Sopenharmony_ci
258662306a36Sopenharmony_ci	if (nla_put(skb, NDA_DST, neigh->tbl->key_len, neigh->primary_key))
258762306a36Sopenharmony_ci		goto nla_put_failure;
258862306a36Sopenharmony_ci
258962306a36Sopenharmony_ci	read_lock_bh(&neigh->lock);
259062306a36Sopenharmony_ci	ndm->ndm_state	 = neigh->nud_state;
259162306a36Sopenharmony_ci	if (neigh->nud_state & NUD_VALID) {
259262306a36Sopenharmony_ci		char haddr[MAX_ADDR_LEN];
259362306a36Sopenharmony_ci
259462306a36Sopenharmony_ci		neigh_ha_snapshot(haddr, neigh, neigh->dev);
259562306a36Sopenharmony_ci		if (nla_put(skb, NDA_LLADDR, neigh->dev->addr_len, haddr) < 0) {
259662306a36Sopenharmony_ci			read_unlock_bh(&neigh->lock);
259762306a36Sopenharmony_ci			goto nla_put_failure;
259862306a36Sopenharmony_ci		}
259962306a36Sopenharmony_ci	}
260062306a36Sopenharmony_ci
260162306a36Sopenharmony_ci	ci.ndm_used	 = jiffies_to_clock_t(now - neigh->used);
260262306a36Sopenharmony_ci	ci.ndm_confirmed = jiffies_to_clock_t(now - neigh->confirmed);
260362306a36Sopenharmony_ci	ci.ndm_updated	 = jiffies_to_clock_t(now - neigh->updated);
260462306a36Sopenharmony_ci	ci.ndm_refcnt	 = refcount_read(&neigh->refcnt) - 1;
260562306a36Sopenharmony_ci	read_unlock_bh(&neigh->lock);
260662306a36Sopenharmony_ci
260762306a36Sopenharmony_ci	if (nla_put_u32(skb, NDA_PROBES, atomic_read(&neigh->probes)) ||
260862306a36Sopenharmony_ci	    nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci))
260962306a36Sopenharmony_ci		goto nla_put_failure;
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_ci	if (neigh->protocol && nla_put_u8(skb, NDA_PROTOCOL, neigh->protocol))
261262306a36Sopenharmony_ci		goto nla_put_failure;
261362306a36Sopenharmony_ci	if (neigh_flags_ext && nla_put_u32(skb, NDA_FLAGS_EXT, neigh_flags_ext))
261462306a36Sopenharmony_ci		goto nla_put_failure;
261562306a36Sopenharmony_ci
261662306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
261762306a36Sopenharmony_ci	return 0;
261862306a36Sopenharmony_ci
261962306a36Sopenharmony_cinla_put_failure:
262062306a36Sopenharmony_ci	nlmsg_cancel(skb, nlh);
262162306a36Sopenharmony_ci	return -EMSGSIZE;
262262306a36Sopenharmony_ci}
262362306a36Sopenharmony_ci
262462306a36Sopenharmony_cistatic int pneigh_fill_info(struct sk_buff *skb, struct pneigh_entry *pn,
262562306a36Sopenharmony_ci			    u32 pid, u32 seq, int type, unsigned int flags,
262662306a36Sopenharmony_ci			    struct neigh_table *tbl)
262762306a36Sopenharmony_ci{
262862306a36Sopenharmony_ci	u32 neigh_flags, neigh_flags_ext;
262962306a36Sopenharmony_ci	struct nlmsghdr *nlh;
263062306a36Sopenharmony_ci	struct ndmsg *ndm;
263162306a36Sopenharmony_ci
263262306a36Sopenharmony_ci	nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags);
263362306a36Sopenharmony_ci	if (nlh == NULL)
263462306a36Sopenharmony_ci		return -EMSGSIZE;
263562306a36Sopenharmony_ci
263662306a36Sopenharmony_ci	neigh_flags_ext = pn->flags >> NTF_EXT_SHIFT;
263762306a36Sopenharmony_ci	neigh_flags     = pn->flags & NTF_OLD_MASK;
263862306a36Sopenharmony_ci
263962306a36Sopenharmony_ci	ndm = nlmsg_data(nlh);
264062306a36Sopenharmony_ci	ndm->ndm_family	 = tbl->family;
264162306a36Sopenharmony_ci	ndm->ndm_pad1    = 0;
264262306a36Sopenharmony_ci	ndm->ndm_pad2    = 0;
264362306a36Sopenharmony_ci	ndm->ndm_flags	 = neigh_flags | NTF_PROXY;
264462306a36Sopenharmony_ci	ndm->ndm_type	 = RTN_UNICAST;
264562306a36Sopenharmony_ci	ndm->ndm_ifindex = pn->dev ? pn->dev->ifindex : 0;
264662306a36Sopenharmony_ci	ndm->ndm_state	 = NUD_NONE;
264762306a36Sopenharmony_ci
264862306a36Sopenharmony_ci	if (nla_put(skb, NDA_DST, tbl->key_len, pn->key))
264962306a36Sopenharmony_ci		goto nla_put_failure;
265062306a36Sopenharmony_ci
265162306a36Sopenharmony_ci	if (pn->protocol && nla_put_u8(skb, NDA_PROTOCOL, pn->protocol))
265262306a36Sopenharmony_ci		goto nla_put_failure;
265362306a36Sopenharmony_ci	if (neigh_flags_ext && nla_put_u32(skb, NDA_FLAGS_EXT, neigh_flags_ext))
265462306a36Sopenharmony_ci		goto nla_put_failure;
265562306a36Sopenharmony_ci
265662306a36Sopenharmony_ci	nlmsg_end(skb, nlh);
265762306a36Sopenharmony_ci	return 0;
265862306a36Sopenharmony_ci
265962306a36Sopenharmony_cinla_put_failure:
266062306a36Sopenharmony_ci	nlmsg_cancel(skb, nlh);
266162306a36Sopenharmony_ci	return -EMSGSIZE;
266262306a36Sopenharmony_ci}
266362306a36Sopenharmony_ci
266462306a36Sopenharmony_cistatic void neigh_update_notify(struct neighbour *neigh, u32 nlmsg_pid)
266562306a36Sopenharmony_ci{
266662306a36Sopenharmony_ci	call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh);
266762306a36Sopenharmony_ci	__neigh_notify(neigh, RTM_NEWNEIGH, 0, nlmsg_pid);
266862306a36Sopenharmony_ci}
266962306a36Sopenharmony_ci
267062306a36Sopenharmony_cistatic bool neigh_master_filtered(struct net_device *dev, int master_idx)
267162306a36Sopenharmony_ci{
267262306a36Sopenharmony_ci	struct net_device *master;
267362306a36Sopenharmony_ci
267462306a36Sopenharmony_ci	if (!master_idx)
267562306a36Sopenharmony_ci		return false;
267662306a36Sopenharmony_ci
267762306a36Sopenharmony_ci	master = dev ? netdev_master_upper_dev_get(dev) : NULL;
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci	/* 0 is already used to denote NDA_MASTER wasn't passed, therefore need another
268062306a36Sopenharmony_ci	 * invalid value for ifindex to denote "no master".
268162306a36Sopenharmony_ci	 */
268262306a36Sopenharmony_ci	if (master_idx == -1)
268362306a36Sopenharmony_ci		return !!master;
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_ci	if (!master || master->ifindex != master_idx)
268662306a36Sopenharmony_ci		return true;
268762306a36Sopenharmony_ci
268862306a36Sopenharmony_ci	return false;
268962306a36Sopenharmony_ci}
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_cistatic bool neigh_ifindex_filtered(struct net_device *dev, int filter_idx)
269262306a36Sopenharmony_ci{
269362306a36Sopenharmony_ci	if (filter_idx && (!dev || dev->ifindex != filter_idx))
269462306a36Sopenharmony_ci		return true;
269562306a36Sopenharmony_ci
269662306a36Sopenharmony_ci	return false;
269762306a36Sopenharmony_ci}
269862306a36Sopenharmony_ci
269962306a36Sopenharmony_cistruct neigh_dump_filter {
270062306a36Sopenharmony_ci	int master_idx;
270162306a36Sopenharmony_ci	int dev_idx;
270262306a36Sopenharmony_ci};
270362306a36Sopenharmony_ci
270462306a36Sopenharmony_cistatic int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
270562306a36Sopenharmony_ci			    struct netlink_callback *cb,
270662306a36Sopenharmony_ci			    struct neigh_dump_filter *filter)
270762306a36Sopenharmony_ci{
270862306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
270962306a36Sopenharmony_ci	struct neighbour *n;
271062306a36Sopenharmony_ci	int rc, h, s_h = cb->args[1];
271162306a36Sopenharmony_ci	int idx, s_idx = idx = cb->args[2];
271262306a36Sopenharmony_ci	struct neigh_hash_table *nht;
271362306a36Sopenharmony_ci	unsigned int flags = NLM_F_MULTI;
271462306a36Sopenharmony_ci
271562306a36Sopenharmony_ci	if (filter->dev_idx || filter->master_idx)
271662306a36Sopenharmony_ci		flags |= NLM_F_DUMP_FILTERED;
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_ci	rcu_read_lock();
271962306a36Sopenharmony_ci	nht = rcu_dereference(tbl->nht);
272062306a36Sopenharmony_ci
272162306a36Sopenharmony_ci	for (h = s_h; h < (1 << nht->hash_shift); h++) {
272262306a36Sopenharmony_ci		if (h > s_h)
272362306a36Sopenharmony_ci			s_idx = 0;
272462306a36Sopenharmony_ci		for (n = rcu_dereference(nht->hash_buckets[h]), idx = 0;
272562306a36Sopenharmony_ci		     n != NULL;
272662306a36Sopenharmony_ci		     n = rcu_dereference(n->next)) {
272762306a36Sopenharmony_ci			if (idx < s_idx || !net_eq(dev_net(n->dev), net))
272862306a36Sopenharmony_ci				goto next;
272962306a36Sopenharmony_ci			if (neigh_ifindex_filtered(n->dev, filter->dev_idx) ||
273062306a36Sopenharmony_ci			    neigh_master_filtered(n->dev, filter->master_idx))
273162306a36Sopenharmony_ci				goto next;
273262306a36Sopenharmony_ci			if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid,
273362306a36Sopenharmony_ci					    cb->nlh->nlmsg_seq,
273462306a36Sopenharmony_ci					    RTM_NEWNEIGH,
273562306a36Sopenharmony_ci					    flags) < 0) {
273662306a36Sopenharmony_ci				rc = -1;
273762306a36Sopenharmony_ci				goto out;
273862306a36Sopenharmony_ci			}
273962306a36Sopenharmony_cinext:
274062306a36Sopenharmony_ci			idx++;
274162306a36Sopenharmony_ci		}
274262306a36Sopenharmony_ci	}
274362306a36Sopenharmony_ci	rc = skb->len;
274462306a36Sopenharmony_ciout:
274562306a36Sopenharmony_ci	rcu_read_unlock();
274662306a36Sopenharmony_ci	cb->args[1] = h;
274762306a36Sopenharmony_ci	cb->args[2] = idx;
274862306a36Sopenharmony_ci	return rc;
274962306a36Sopenharmony_ci}
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_cistatic int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
275262306a36Sopenharmony_ci			     struct netlink_callback *cb,
275362306a36Sopenharmony_ci			     struct neigh_dump_filter *filter)
275462306a36Sopenharmony_ci{
275562306a36Sopenharmony_ci	struct pneigh_entry *n;
275662306a36Sopenharmony_ci	struct net *net = sock_net(skb->sk);
275762306a36Sopenharmony_ci	int rc, h, s_h = cb->args[3];
275862306a36Sopenharmony_ci	int idx, s_idx = idx = cb->args[4];
275962306a36Sopenharmony_ci	unsigned int flags = NLM_F_MULTI;
276062306a36Sopenharmony_ci
276162306a36Sopenharmony_ci	if (filter->dev_idx || filter->master_idx)
276262306a36Sopenharmony_ci		flags |= NLM_F_DUMP_FILTERED;
276362306a36Sopenharmony_ci
276462306a36Sopenharmony_ci	read_lock_bh(&tbl->lock);
276562306a36Sopenharmony_ci
276662306a36Sopenharmony_ci	for (h = s_h; h <= PNEIGH_HASHMASK; h++) {
276762306a36Sopenharmony_ci		if (h > s_h)
276862306a36Sopenharmony_ci			s_idx = 0;
276962306a36Sopenharmony_ci		for (n = tbl->phash_buckets[h], idx = 0; n; n = n->next) {
277062306a36Sopenharmony_ci			if (idx < s_idx || pneigh_net(n) != net)
277162306a36Sopenharmony_ci				goto next;
277262306a36Sopenharmony_ci			if (neigh_ifindex_filtered(n->dev, filter->dev_idx) ||
277362306a36Sopenharmony_ci			    neigh_master_filtered(n->dev, filter->master_idx))
277462306a36Sopenharmony_ci				goto next;
277562306a36Sopenharmony_ci			if (pneigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid,
277662306a36Sopenharmony_ci					    cb->nlh->nlmsg_seq,
277762306a36Sopenharmony_ci					    RTM_NEWNEIGH, flags, tbl) < 0) {
277862306a36Sopenharmony_ci				read_unlock_bh(&tbl->lock);
277962306a36Sopenharmony_ci				rc = -1;
278062306a36Sopenharmony_ci				goto out;
278162306a36Sopenharmony_ci			}
278262306a36Sopenharmony_ci		next:
278362306a36Sopenharmony_ci			idx++;
278462306a36Sopenharmony_ci		}
278562306a36Sopenharmony_ci	}
278662306a36Sopenharmony_ci
278762306a36Sopenharmony_ci	read_unlock_bh(&tbl->lock);
278862306a36Sopenharmony_ci	rc = skb->len;
278962306a36Sopenharmony_ciout:
279062306a36Sopenharmony_ci	cb->args[3] = h;
279162306a36Sopenharmony_ci	cb->args[4] = idx;
279262306a36Sopenharmony_ci	return rc;
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_ci}
279562306a36Sopenharmony_ci
279662306a36Sopenharmony_cistatic int neigh_valid_dump_req(const struct nlmsghdr *nlh,
279762306a36Sopenharmony_ci				bool strict_check,
279862306a36Sopenharmony_ci				struct neigh_dump_filter *filter,
279962306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
280062306a36Sopenharmony_ci{
280162306a36Sopenharmony_ci	struct nlattr *tb[NDA_MAX + 1];
280262306a36Sopenharmony_ci	int err, i;
280362306a36Sopenharmony_ci
280462306a36Sopenharmony_ci	if (strict_check) {
280562306a36Sopenharmony_ci		struct ndmsg *ndm;
280662306a36Sopenharmony_ci
280762306a36Sopenharmony_ci		if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) {
280862306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid header for neighbor dump request");
280962306a36Sopenharmony_ci			return -EINVAL;
281062306a36Sopenharmony_ci		}
281162306a36Sopenharmony_ci
281262306a36Sopenharmony_ci		ndm = nlmsg_data(nlh);
281362306a36Sopenharmony_ci		if (ndm->ndm_pad1  || ndm->ndm_pad2  || ndm->ndm_ifindex ||
281462306a36Sopenharmony_ci		    ndm->ndm_state || ndm->ndm_type) {
281562306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor dump request");
281662306a36Sopenharmony_ci			return -EINVAL;
281762306a36Sopenharmony_ci		}
281862306a36Sopenharmony_ci
281962306a36Sopenharmony_ci		if (ndm->ndm_flags & ~NTF_PROXY) {
282062306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor dump request");
282162306a36Sopenharmony_ci			return -EINVAL;
282262306a36Sopenharmony_ci		}
282362306a36Sopenharmony_ci
282462306a36Sopenharmony_ci		err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct ndmsg),
282562306a36Sopenharmony_ci						    tb, NDA_MAX, nda_policy,
282662306a36Sopenharmony_ci						    extack);
282762306a36Sopenharmony_ci	} else {
282862306a36Sopenharmony_ci		err = nlmsg_parse_deprecated(nlh, sizeof(struct ndmsg), tb,
282962306a36Sopenharmony_ci					     NDA_MAX, nda_policy, extack);
283062306a36Sopenharmony_ci	}
283162306a36Sopenharmony_ci	if (err < 0)
283262306a36Sopenharmony_ci		return err;
283362306a36Sopenharmony_ci
283462306a36Sopenharmony_ci	for (i = 0; i <= NDA_MAX; ++i) {
283562306a36Sopenharmony_ci		if (!tb[i])
283662306a36Sopenharmony_ci			continue;
283762306a36Sopenharmony_ci
283862306a36Sopenharmony_ci		/* all new attributes should require strict_check */
283962306a36Sopenharmony_ci		switch (i) {
284062306a36Sopenharmony_ci		case NDA_IFINDEX:
284162306a36Sopenharmony_ci			filter->dev_idx = nla_get_u32(tb[i]);
284262306a36Sopenharmony_ci			break;
284362306a36Sopenharmony_ci		case NDA_MASTER:
284462306a36Sopenharmony_ci			filter->master_idx = nla_get_u32(tb[i]);
284562306a36Sopenharmony_ci			break;
284662306a36Sopenharmony_ci		default:
284762306a36Sopenharmony_ci			if (strict_check) {
284862306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor dump request");
284962306a36Sopenharmony_ci				return -EINVAL;
285062306a36Sopenharmony_ci			}
285162306a36Sopenharmony_ci		}
285262306a36Sopenharmony_ci	}
285362306a36Sopenharmony_ci
285462306a36Sopenharmony_ci	return 0;
285562306a36Sopenharmony_ci}
285662306a36Sopenharmony_ci
285762306a36Sopenharmony_cistatic int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
285862306a36Sopenharmony_ci{
285962306a36Sopenharmony_ci	const struct nlmsghdr *nlh = cb->nlh;
286062306a36Sopenharmony_ci	struct neigh_dump_filter filter = {};
286162306a36Sopenharmony_ci	struct neigh_table *tbl;
286262306a36Sopenharmony_ci	int t, family, s_t;
286362306a36Sopenharmony_ci	int proxy = 0;
286462306a36Sopenharmony_ci	int err;
286562306a36Sopenharmony_ci
286662306a36Sopenharmony_ci	family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family;
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_ci	/* check for full ndmsg structure presence, family member is
286962306a36Sopenharmony_ci	 * the same for both structures
287062306a36Sopenharmony_ci	 */
287162306a36Sopenharmony_ci	if (nlmsg_len(nlh) >= sizeof(struct ndmsg) &&
287262306a36Sopenharmony_ci	    ((struct ndmsg *)nlmsg_data(nlh))->ndm_flags == NTF_PROXY)
287362306a36Sopenharmony_ci		proxy = 1;
287462306a36Sopenharmony_ci
287562306a36Sopenharmony_ci	err = neigh_valid_dump_req(nlh, cb->strict_check, &filter, cb->extack);
287662306a36Sopenharmony_ci	if (err < 0 && cb->strict_check)
287762306a36Sopenharmony_ci		return err;
287862306a36Sopenharmony_ci
287962306a36Sopenharmony_ci	s_t = cb->args[0];
288062306a36Sopenharmony_ci
288162306a36Sopenharmony_ci	for (t = 0; t < NEIGH_NR_TABLES; t++) {
288262306a36Sopenharmony_ci		tbl = neigh_tables[t];
288362306a36Sopenharmony_ci
288462306a36Sopenharmony_ci		if (!tbl)
288562306a36Sopenharmony_ci			continue;
288662306a36Sopenharmony_ci		if (t < s_t || (family && tbl->family != family))
288762306a36Sopenharmony_ci			continue;
288862306a36Sopenharmony_ci		if (t > s_t)
288962306a36Sopenharmony_ci			memset(&cb->args[1], 0, sizeof(cb->args) -
289062306a36Sopenharmony_ci						sizeof(cb->args[0]));
289162306a36Sopenharmony_ci		if (proxy)
289262306a36Sopenharmony_ci			err = pneigh_dump_table(tbl, skb, cb, &filter);
289362306a36Sopenharmony_ci		else
289462306a36Sopenharmony_ci			err = neigh_dump_table(tbl, skb, cb, &filter);
289562306a36Sopenharmony_ci		if (err < 0)
289662306a36Sopenharmony_ci			break;
289762306a36Sopenharmony_ci	}
289862306a36Sopenharmony_ci
289962306a36Sopenharmony_ci	cb->args[0] = t;
290062306a36Sopenharmony_ci	return skb->len;
290162306a36Sopenharmony_ci}
290262306a36Sopenharmony_ci
290362306a36Sopenharmony_cistatic int neigh_valid_get_req(const struct nlmsghdr *nlh,
290462306a36Sopenharmony_ci			       struct neigh_table **tbl,
290562306a36Sopenharmony_ci			       void **dst, int *dev_idx, u8 *ndm_flags,
290662306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
290762306a36Sopenharmony_ci{
290862306a36Sopenharmony_ci	struct nlattr *tb[NDA_MAX + 1];
290962306a36Sopenharmony_ci	struct ndmsg *ndm;
291062306a36Sopenharmony_ci	int err, i;
291162306a36Sopenharmony_ci
291262306a36Sopenharmony_ci	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) {
291362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid header for neighbor get request");
291462306a36Sopenharmony_ci		return -EINVAL;
291562306a36Sopenharmony_ci	}
291662306a36Sopenharmony_ci
291762306a36Sopenharmony_ci	ndm = nlmsg_data(nlh);
291862306a36Sopenharmony_ci	if (ndm->ndm_pad1  || ndm->ndm_pad2  || ndm->ndm_state ||
291962306a36Sopenharmony_ci	    ndm->ndm_type) {
292062306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor get request");
292162306a36Sopenharmony_ci		return -EINVAL;
292262306a36Sopenharmony_ci	}
292362306a36Sopenharmony_ci
292462306a36Sopenharmony_ci	if (ndm->ndm_flags & ~NTF_PROXY) {
292562306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor get request");
292662306a36Sopenharmony_ci		return -EINVAL;
292762306a36Sopenharmony_ci	}
292862306a36Sopenharmony_ci
292962306a36Sopenharmony_ci	err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct ndmsg), tb,
293062306a36Sopenharmony_ci					    NDA_MAX, nda_policy, extack);
293162306a36Sopenharmony_ci	if (err < 0)
293262306a36Sopenharmony_ci		return err;
293362306a36Sopenharmony_ci
293462306a36Sopenharmony_ci	*ndm_flags = ndm->ndm_flags;
293562306a36Sopenharmony_ci	*dev_idx = ndm->ndm_ifindex;
293662306a36Sopenharmony_ci	*tbl = neigh_find_table(ndm->ndm_family);
293762306a36Sopenharmony_ci	if (*tbl == NULL) {
293862306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Unsupported family in header for neighbor get request");
293962306a36Sopenharmony_ci		return -EAFNOSUPPORT;
294062306a36Sopenharmony_ci	}
294162306a36Sopenharmony_ci
294262306a36Sopenharmony_ci	for (i = 0; i <= NDA_MAX; ++i) {
294362306a36Sopenharmony_ci		if (!tb[i])
294462306a36Sopenharmony_ci			continue;
294562306a36Sopenharmony_ci
294662306a36Sopenharmony_ci		switch (i) {
294762306a36Sopenharmony_ci		case NDA_DST:
294862306a36Sopenharmony_ci			if (nla_len(tb[i]) != (int)(*tbl)->key_len) {
294962306a36Sopenharmony_ci				NL_SET_ERR_MSG(extack, "Invalid network address in neighbor get request");
295062306a36Sopenharmony_ci				return -EINVAL;
295162306a36Sopenharmony_ci			}
295262306a36Sopenharmony_ci			*dst = nla_data(tb[i]);
295362306a36Sopenharmony_ci			break;
295462306a36Sopenharmony_ci		default:
295562306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor get request");
295662306a36Sopenharmony_ci			return -EINVAL;
295762306a36Sopenharmony_ci		}
295862306a36Sopenharmony_ci	}
295962306a36Sopenharmony_ci
296062306a36Sopenharmony_ci	return 0;
296162306a36Sopenharmony_ci}
296262306a36Sopenharmony_ci
296362306a36Sopenharmony_cistatic inline size_t neigh_nlmsg_size(void)
296462306a36Sopenharmony_ci{
296562306a36Sopenharmony_ci	return NLMSG_ALIGN(sizeof(struct ndmsg))
296662306a36Sopenharmony_ci	       + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */
296762306a36Sopenharmony_ci	       + nla_total_size(MAX_ADDR_LEN) /* NDA_LLADDR */
296862306a36Sopenharmony_ci	       + nla_total_size(sizeof(struct nda_cacheinfo))
296962306a36Sopenharmony_ci	       + nla_total_size(4)  /* NDA_PROBES */
297062306a36Sopenharmony_ci	       + nla_total_size(4)  /* NDA_FLAGS_EXT */
297162306a36Sopenharmony_ci	       + nla_total_size(1); /* NDA_PROTOCOL */
297262306a36Sopenharmony_ci}
297362306a36Sopenharmony_ci
297462306a36Sopenharmony_cistatic int neigh_get_reply(struct net *net, struct neighbour *neigh,
297562306a36Sopenharmony_ci			   u32 pid, u32 seq)
297662306a36Sopenharmony_ci{
297762306a36Sopenharmony_ci	struct sk_buff *skb;
297862306a36Sopenharmony_ci	int err = 0;
297962306a36Sopenharmony_ci
298062306a36Sopenharmony_ci	skb = nlmsg_new(neigh_nlmsg_size(), GFP_KERNEL);
298162306a36Sopenharmony_ci	if (!skb)
298262306a36Sopenharmony_ci		return -ENOBUFS;
298362306a36Sopenharmony_ci
298462306a36Sopenharmony_ci	err = neigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0);
298562306a36Sopenharmony_ci	if (err) {
298662306a36Sopenharmony_ci		kfree_skb(skb);
298762306a36Sopenharmony_ci		goto errout;
298862306a36Sopenharmony_ci	}
298962306a36Sopenharmony_ci
299062306a36Sopenharmony_ci	err = rtnl_unicast(skb, net, pid);
299162306a36Sopenharmony_cierrout:
299262306a36Sopenharmony_ci	return err;
299362306a36Sopenharmony_ci}
299462306a36Sopenharmony_ci
299562306a36Sopenharmony_cistatic inline size_t pneigh_nlmsg_size(void)
299662306a36Sopenharmony_ci{
299762306a36Sopenharmony_ci	return NLMSG_ALIGN(sizeof(struct ndmsg))
299862306a36Sopenharmony_ci	       + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */
299962306a36Sopenharmony_ci	       + nla_total_size(4)  /* NDA_FLAGS_EXT */
300062306a36Sopenharmony_ci	       + nla_total_size(1); /* NDA_PROTOCOL */
300162306a36Sopenharmony_ci}
300262306a36Sopenharmony_ci
300362306a36Sopenharmony_cistatic int pneigh_get_reply(struct net *net, struct pneigh_entry *neigh,
300462306a36Sopenharmony_ci			    u32 pid, u32 seq, struct neigh_table *tbl)
300562306a36Sopenharmony_ci{
300662306a36Sopenharmony_ci	struct sk_buff *skb;
300762306a36Sopenharmony_ci	int err = 0;
300862306a36Sopenharmony_ci
300962306a36Sopenharmony_ci	skb = nlmsg_new(pneigh_nlmsg_size(), GFP_KERNEL);
301062306a36Sopenharmony_ci	if (!skb)
301162306a36Sopenharmony_ci		return -ENOBUFS;
301262306a36Sopenharmony_ci
301362306a36Sopenharmony_ci	err = pneigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0, tbl);
301462306a36Sopenharmony_ci	if (err) {
301562306a36Sopenharmony_ci		kfree_skb(skb);
301662306a36Sopenharmony_ci		goto errout;
301762306a36Sopenharmony_ci	}
301862306a36Sopenharmony_ci
301962306a36Sopenharmony_ci	err = rtnl_unicast(skb, net, pid);
302062306a36Sopenharmony_cierrout:
302162306a36Sopenharmony_ci	return err;
302262306a36Sopenharmony_ci}
302362306a36Sopenharmony_ci
302462306a36Sopenharmony_cistatic int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh,
302562306a36Sopenharmony_ci		     struct netlink_ext_ack *extack)
302662306a36Sopenharmony_ci{
302762306a36Sopenharmony_ci	struct net *net = sock_net(in_skb->sk);
302862306a36Sopenharmony_ci	struct net_device *dev = NULL;
302962306a36Sopenharmony_ci	struct neigh_table *tbl = NULL;
303062306a36Sopenharmony_ci	struct neighbour *neigh;
303162306a36Sopenharmony_ci	void *dst = NULL;
303262306a36Sopenharmony_ci	u8 ndm_flags = 0;
303362306a36Sopenharmony_ci	int dev_idx = 0;
303462306a36Sopenharmony_ci	int err;
303562306a36Sopenharmony_ci
303662306a36Sopenharmony_ci	err = neigh_valid_get_req(nlh, &tbl, &dst, &dev_idx, &ndm_flags,
303762306a36Sopenharmony_ci				  extack);
303862306a36Sopenharmony_ci	if (err < 0)
303962306a36Sopenharmony_ci		return err;
304062306a36Sopenharmony_ci
304162306a36Sopenharmony_ci	if (dev_idx) {
304262306a36Sopenharmony_ci		dev = __dev_get_by_index(net, dev_idx);
304362306a36Sopenharmony_ci		if (!dev) {
304462306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Unknown device ifindex");
304562306a36Sopenharmony_ci			return -ENODEV;
304662306a36Sopenharmony_ci		}
304762306a36Sopenharmony_ci	}
304862306a36Sopenharmony_ci
304962306a36Sopenharmony_ci	if (!dst) {
305062306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Network address not specified");
305162306a36Sopenharmony_ci		return -EINVAL;
305262306a36Sopenharmony_ci	}
305362306a36Sopenharmony_ci
305462306a36Sopenharmony_ci	if (ndm_flags & NTF_PROXY) {
305562306a36Sopenharmony_ci		struct pneigh_entry *pn;
305662306a36Sopenharmony_ci
305762306a36Sopenharmony_ci		pn = pneigh_lookup(tbl, net, dst, dev, 0);
305862306a36Sopenharmony_ci		if (!pn) {
305962306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Proxy neighbour entry not found");
306062306a36Sopenharmony_ci			return -ENOENT;
306162306a36Sopenharmony_ci		}
306262306a36Sopenharmony_ci		return pneigh_get_reply(net, pn, NETLINK_CB(in_skb).portid,
306362306a36Sopenharmony_ci					nlh->nlmsg_seq, tbl);
306462306a36Sopenharmony_ci	}
306562306a36Sopenharmony_ci
306662306a36Sopenharmony_ci	if (!dev) {
306762306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "No device specified");
306862306a36Sopenharmony_ci		return -EINVAL;
306962306a36Sopenharmony_ci	}
307062306a36Sopenharmony_ci
307162306a36Sopenharmony_ci	neigh = neigh_lookup(tbl, dst, dev);
307262306a36Sopenharmony_ci	if (!neigh) {
307362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Neighbour entry not found");
307462306a36Sopenharmony_ci		return -ENOENT;
307562306a36Sopenharmony_ci	}
307662306a36Sopenharmony_ci
307762306a36Sopenharmony_ci	err = neigh_get_reply(net, neigh, NETLINK_CB(in_skb).portid,
307862306a36Sopenharmony_ci			      nlh->nlmsg_seq);
307962306a36Sopenharmony_ci
308062306a36Sopenharmony_ci	neigh_release(neigh);
308162306a36Sopenharmony_ci
308262306a36Sopenharmony_ci	return err;
308362306a36Sopenharmony_ci}
308462306a36Sopenharmony_ci
308562306a36Sopenharmony_civoid neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie)
308662306a36Sopenharmony_ci{
308762306a36Sopenharmony_ci	int chain;
308862306a36Sopenharmony_ci	struct neigh_hash_table *nht;
308962306a36Sopenharmony_ci
309062306a36Sopenharmony_ci	rcu_read_lock();
309162306a36Sopenharmony_ci	nht = rcu_dereference(tbl->nht);
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_ci	read_lock_bh(&tbl->lock); /* avoid resizes */
309462306a36Sopenharmony_ci	for (chain = 0; chain < (1 << nht->hash_shift); chain++) {
309562306a36Sopenharmony_ci		struct neighbour *n;
309662306a36Sopenharmony_ci
309762306a36Sopenharmony_ci		for (n = rcu_dereference(nht->hash_buckets[chain]);
309862306a36Sopenharmony_ci		     n != NULL;
309962306a36Sopenharmony_ci		     n = rcu_dereference(n->next))
310062306a36Sopenharmony_ci			cb(n, cookie);
310162306a36Sopenharmony_ci	}
310262306a36Sopenharmony_ci	read_unlock_bh(&tbl->lock);
310362306a36Sopenharmony_ci	rcu_read_unlock();
310462306a36Sopenharmony_ci}
310562306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_for_each);
310662306a36Sopenharmony_ci
310762306a36Sopenharmony_ci/* The tbl->lock must be held as a writer and BH disabled. */
310862306a36Sopenharmony_civoid __neigh_for_each_release(struct neigh_table *tbl,
310962306a36Sopenharmony_ci			      int (*cb)(struct neighbour *))
311062306a36Sopenharmony_ci{
311162306a36Sopenharmony_ci	int chain;
311262306a36Sopenharmony_ci	struct neigh_hash_table *nht;
311362306a36Sopenharmony_ci
311462306a36Sopenharmony_ci	nht = rcu_dereference_protected(tbl->nht,
311562306a36Sopenharmony_ci					lockdep_is_held(&tbl->lock));
311662306a36Sopenharmony_ci	for (chain = 0; chain < (1 << nht->hash_shift); chain++) {
311762306a36Sopenharmony_ci		struct neighbour *n;
311862306a36Sopenharmony_ci		struct neighbour __rcu **np;
311962306a36Sopenharmony_ci
312062306a36Sopenharmony_ci		np = &nht->hash_buckets[chain];
312162306a36Sopenharmony_ci		while ((n = rcu_dereference_protected(*np,
312262306a36Sopenharmony_ci					lockdep_is_held(&tbl->lock))) != NULL) {
312362306a36Sopenharmony_ci			int release;
312462306a36Sopenharmony_ci
312562306a36Sopenharmony_ci			write_lock(&n->lock);
312662306a36Sopenharmony_ci			release = cb(n);
312762306a36Sopenharmony_ci			if (release) {
312862306a36Sopenharmony_ci				rcu_assign_pointer(*np,
312962306a36Sopenharmony_ci					rcu_dereference_protected(n->next,
313062306a36Sopenharmony_ci						lockdep_is_held(&tbl->lock)));
313162306a36Sopenharmony_ci				neigh_mark_dead(n);
313262306a36Sopenharmony_ci			} else
313362306a36Sopenharmony_ci				np = &n->next;
313462306a36Sopenharmony_ci			write_unlock(&n->lock);
313562306a36Sopenharmony_ci			if (release)
313662306a36Sopenharmony_ci				neigh_cleanup_and_release(n);
313762306a36Sopenharmony_ci		}
313862306a36Sopenharmony_ci	}
313962306a36Sopenharmony_ci}
314062306a36Sopenharmony_ciEXPORT_SYMBOL(__neigh_for_each_release);
314162306a36Sopenharmony_ci
314262306a36Sopenharmony_ciint neigh_xmit(int index, struct net_device *dev,
314362306a36Sopenharmony_ci	       const void *addr, struct sk_buff *skb)
314462306a36Sopenharmony_ci{
314562306a36Sopenharmony_ci	int err = -EAFNOSUPPORT;
314662306a36Sopenharmony_ci	if (likely(index < NEIGH_NR_TABLES)) {
314762306a36Sopenharmony_ci		struct neigh_table *tbl;
314862306a36Sopenharmony_ci		struct neighbour *neigh;
314962306a36Sopenharmony_ci
315062306a36Sopenharmony_ci		tbl = neigh_tables[index];
315162306a36Sopenharmony_ci		if (!tbl)
315262306a36Sopenharmony_ci			goto out;
315362306a36Sopenharmony_ci		rcu_read_lock();
315462306a36Sopenharmony_ci		if (index == NEIGH_ARP_TABLE) {
315562306a36Sopenharmony_ci			u32 key = *((u32 *)addr);
315662306a36Sopenharmony_ci
315762306a36Sopenharmony_ci			neigh = __ipv4_neigh_lookup_noref(dev, key);
315862306a36Sopenharmony_ci		} else {
315962306a36Sopenharmony_ci			neigh = __neigh_lookup_noref(tbl, addr, dev);
316062306a36Sopenharmony_ci		}
316162306a36Sopenharmony_ci		if (!neigh)
316262306a36Sopenharmony_ci			neigh = __neigh_create(tbl, addr, dev, false);
316362306a36Sopenharmony_ci		err = PTR_ERR(neigh);
316462306a36Sopenharmony_ci		if (IS_ERR(neigh)) {
316562306a36Sopenharmony_ci			rcu_read_unlock();
316662306a36Sopenharmony_ci			goto out_kfree_skb;
316762306a36Sopenharmony_ci		}
316862306a36Sopenharmony_ci		err = READ_ONCE(neigh->output)(neigh, skb);
316962306a36Sopenharmony_ci		rcu_read_unlock();
317062306a36Sopenharmony_ci	}
317162306a36Sopenharmony_ci	else if (index == NEIGH_LINK_TABLE) {
317262306a36Sopenharmony_ci		err = dev_hard_header(skb, dev, ntohs(skb->protocol),
317362306a36Sopenharmony_ci				      addr, NULL, skb->len);
317462306a36Sopenharmony_ci		if (err < 0)
317562306a36Sopenharmony_ci			goto out_kfree_skb;
317662306a36Sopenharmony_ci		err = dev_queue_xmit(skb);
317762306a36Sopenharmony_ci	}
317862306a36Sopenharmony_ciout:
317962306a36Sopenharmony_ci	return err;
318062306a36Sopenharmony_ciout_kfree_skb:
318162306a36Sopenharmony_ci	kfree_skb(skb);
318262306a36Sopenharmony_ci	goto out;
318362306a36Sopenharmony_ci}
318462306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_xmit);
318562306a36Sopenharmony_ci
318662306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
318762306a36Sopenharmony_ci
318862306a36Sopenharmony_cistatic struct neighbour *neigh_get_first(struct seq_file *seq)
318962306a36Sopenharmony_ci{
319062306a36Sopenharmony_ci	struct neigh_seq_state *state = seq->private;
319162306a36Sopenharmony_ci	struct net *net = seq_file_net(seq);
319262306a36Sopenharmony_ci	struct neigh_hash_table *nht = state->nht;
319362306a36Sopenharmony_ci	struct neighbour *n = NULL;
319462306a36Sopenharmony_ci	int bucket;
319562306a36Sopenharmony_ci
319662306a36Sopenharmony_ci	state->flags &= ~NEIGH_SEQ_IS_PNEIGH;
319762306a36Sopenharmony_ci	for (bucket = 0; bucket < (1 << nht->hash_shift); bucket++) {
319862306a36Sopenharmony_ci		n = rcu_dereference(nht->hash_buckets[bucket]);
319962306a36Sopenharmony_ci
320062306a36Sopenharmony_ci		while (n) {
320162306a36Sopenharmony_ci			if (!net_eq(dev_net(n->dev), net))
320262306a36Sopenharmony_ci				goto next;
320362306a36Sopenharmony_ci			if (state->neigh_sub_iter) {
320462306a36Sopenharmony_ci				loff_t fakep = 0;
320562306a36Sopenharmony_ci				void *v;
320662306a36Sopenharmony_ci
320762306a36Sopenharmony_ci				v = state->neigh_sub_iter(state, n, &fakep);
320862306a36Sopenharmony_ci				if (!v)
320962306a36Sopenharmony_ci					goto next;
321062306a36Sopenharmony_ci			}
321162306a36Sopenharmony_ci			if (!(state->flags & NEIGH_SEQ_SKIP_NOARP))
321262306a36Sopenharmony_ci				break;
321362306a36Sopenharmony_ci			if (READ_ONCE(n->nud_state) & ~NUD_NOARP)
321462306a36Sopenharmony_ci				break;
321562306a36Sopenharmony_cinext:
321662306a36Sopenharmony_ci			n = rcu_dereference(n->next);
321762306a36Sopenharmony_ci		}
321862306a36Sopenharmony_ci
321962306a36Sopenharmony_ci		if (n)
322062306a36Sopenharmony_ci			break;
322162306a36Sopenharmony_ci	}
322262306a36Sopenharmony_ci	state->bucket = bucket;
322362306a36Sopenharmony_ci
322462306a36Sopenharmony_ci	return n;
322562306a36Sopenharmony_ci}
322662306a36Sopenharmony_ci
322762306a36Sopenharmony_cistatic struct neighbour *neigh_get_next(struct seq_file *seq,
322862306a36Sopenharmony_ci					struct neighbour *n,
322962306a36Sopenharmony_ci					loff_t *pos)
323062306a36Sopenharmony_ci{
323162306a36Sopenharmony_ci	struct neigh_seq_state *state = seq->private;
323262306a36Sopenharmony_ci	struct net *net = seq_file_net(seq);
323362306a36Sopenharmony_ci	struct neigh_hash_table *nht = state->nht;
323462306a36Sopenharmony_ci
323562306a36Sopenharmony_ci	if (state->neigh_sub_iter) {
323662306a36Sopenharmony_ci		void *v = state->neigh_sub_iter(state, n, pos);
323762306a36Sopenharmony_ci		if (v)
323862306a36Sopenharmony_ci			return n;
323962306a36Sopenharmony_ci	}
324062306a36Sopenharmony_ci	n = rcu_dereference(n->next);
324162306a36Sopenharmony_ci
324262306a36Sopenharmony_ci	while (1) {
324362306a36Sopenharmony_ci		while (n) {
324462306a36Sopenharmony_ci			if (!net_eq(dev_net(n->dev), net))
324562306a36Sopenharmony_ci				goto next;
324662306a36Sopenharmony_ci			if (state->neigh_sub_iter) {
324762306a36Sopenharmony_ci				void *v = state->neigh_sub_iter(state, n, pos);
324862306a36Sopenharmony_ci				if (v)
324962306a36Sopenharmony_ci					return n;
325062306a36Sopenharmony_ci				goto next;
325162306a36Sopenharmony_ci			}
325262306a36Sopenharmony_ci			if (!(state->flags & NEIGH_SEQ_SKIP_NOARP))
325362306a36Sopenharmony_ci				break;
325462306a36Sopenharmony_ci
325562306a36Sopenharmony_ci			if (READ_ONCE(n->nud_state) & ~NUD_NOARP)
325662306a36Sopenharmony_ci				break;
325762306a36Sopenharmony_cinext:
325862306a36Sopenharmony_ci			n = rcu_dereference(n->next);
325962306a36Sopenharmony_ci		}
326062306a36Sopenharmony_ci
326162306a36Sopenharmony_ci		if (n)
326262306a36Sopenharmony_ci			break;
326362306a36Sopenharmony_ci
326462306a36Sopenharmony_ci		if (++state->bucket >= (1 << nht->hash_shift))
326562306a36Sopenharmony_ci			break;
326662306a36Sopenharmony_ci
326762306a36Sopenharmony_ci		n = rcu_dereference(nht->hash_buckets[state->bucket]);
326862306a36Sopenharmony_ci	}
326962306a36Sopenharmony_ci
327062306a36Sopenharmony_ci	if (n && pos)
327162306a36Sopenharmony_ci		--(*pos);
327262306a36Sopenharmony_ci	return n;
327362306a36Sopenharmony_ci}
327462306a36Sopenharmony_ci
327562306a36Sopenharmony_cistatic struct neighbour *neigh_get_idx(struct seq_file *seq, loff_t *pos)
327662306a36Sopenharmony_ci{
327762306a36Sopenharmony_ci	struct neighbour *n = neigh_get_first(seq);
327862306a36Sopenharmony_ci
327962306a36Sopenharmony_ci	if (n) {
328062306a36Sopenharmony_ci		--(*pos);
328162306a36Sopenharmony_ci		while (*pos) {
328262306a36Sopenharmony_ci			n = neigh_get_next(seq, n, pos);
328362306a36Sopenharmony_ci			if (!n)
328462306a36Sopenharmony_ci				break;
328562306a36Sopenharmony_ci		}
328662306a36Sopenharmony_ci	}
328762306a36Sopenharmony_ci	return *pos ? NULL : n;
328862306a36Sopenharmony_ci}
328962306a36Sopenharmony_ci
329062306a36Sopenharmony_cistatic struct pneigh_entry *pneigh_get_first(struct seq_file *seq)
329162306a36Sopenharmony_ci{
329262306a36Sopenharmony_ci	struct neigh_seq_state *state = seq->private;
329362306a36Sopenharmony_ci	struct net *net = seq_file_net(seq);
329462306a36Sopenharmony_ci	struct neigh_table *tbl = state->tbl;
329562306a36Sopenharmony_ci	struct pneigh_entry *pn = NULL;
329662306a36Sopenharmony_ci	int bucket;
329762306a36Sopenharmony_ci
329862306a36Sopenharmony_ci	state->flags |= NEIGH_SEQ_IS_PNEIGH;
329962306a36Sopenharmony_ci	for (bucket = 0; bucket <= PNEIGH_HASHMASK; bucket++) {
330062306a36Sopenharmony_ci		pn = tbl->phash_buckets[bucket];
330162306a36Sopenharmony_ci		while (pn && !net_eq(pneigh_net(pn), net))
330262306a36Sopenharmony_ci			pn = pn->next;
330362306a36Sopenharmony_ci		if (pn)
330462306a36Sopenharmony_ci			break;
330562306a36Sopenharmony_ci	}
330662306a36Sopenharmony_ci	state->bucket = bucket;
330762306a36Sopenharmony_ci
330862306a36Sopenharmony_ci	return pn;
330962306a36Sopenharmony_ci}
331062306a36Sopenharmony_ci
331162306a36Sopenharmony_cistatic struct pneigh_entry *pneigh_get_next(struct seq_file *seq,
331262306a36Sopenharmony_ci					    struct pneigh_entry *pn,
331362306a36Sopenharmony_ci					    loff_t *pos)
331462306a36Sopenharmony_ci{
331562306a36Sopenharmony_ci	struct neigh_seq_state *state = seq->private;
331662306a36Sopenharmony_ci	struct net *net = seq_file_net(seq);
331762306a36Sopenharmony_ci	struct neigh_table *tbl = state->tbl;
331862306a36Sopenharmony_ci
331962306a36Sopenharmony_ci	do {
332062306a36Sopenharmony_ci		pn = pn->next;
332162306a36Sopenharmony_ci	} while (pn && !net_eq(pneigh_net(pn), net));
332262306a36Sopenharmony_ci
332362306a36Sopenharmony_ci	while (!pn) {
332462306a36Sopenharmony_ci		if (++state->bucket > PNEIGH_HASHMASK)
332562306a36Sopenharmony_ci			break;
332662306a36Sopenharmony_ci		pn = tbl->phash_buckets[state->bucket];
332762306a36Sopenharmony_ci		while (pn && !net_eq(pneigh_net(pn), net))
332862306a36Sopenharmony_ci			pn = pn->next;
332962306a36Sopenharmony_ci		if (pn)
333062306a36Sopenharmony_ci			break;
333162306a36Sopenharmony_ci	}
333262306a36Sopenharmony_ci
333362306a36Sopenharmony_ci	if (pn && pos)
333462306a36Sopenharmony_ci		--(*pos);
333562306a36Sopenharmony_ci
333662306a36Sopenharmony_ci	return pn;
333762306a36Sopenharmony_ci}
333862306a36Sopenharmony_ci
333962306a36Sopenharmony_cistatic struct pneigh_entry *pneigh_get_idx(struct seq_file *seq, loff_t *pos)
334062306a36Sopenharmony_ci{
334162306a36Sopenharmony_ci	struct pneigh_entry *pn = pneigh_get_first(seq);
334262306a36Sopenharmony_ci
334362306a36Sopenharmony_ci	if (pn) {
334462306a36Sopenharmony_ci		--(*pos);
334562306a36Sopenharmony_ci		while (*pos) {
334662306a36Sopenharmony_ci			pn = pneigh_get_next(seq, pn, pos);
334762306a36Sopenharmony_ci			if (!pn)
334862306a36Sopenharmony_ci				break;
334962306a36Sopenharmony_ci		}
335062306a36Sopenharmony_ci	}
335162306a36Sopenharmony_ci	return *pos ? NULL : pn;
335262306a36Sopenharmony_ci}
335362306a36Sopenharmony_ci
335462306a36Sopenharmony_cistatic void *neigh_get_idx_any(struct seq_file *seq, loff_t *pos)
335562306a36Sopenharmony_ci{
335662306a36Sopenharmony_ci	struct neigh_seq_state *state = seq->private;
335762306a36Sopenharmony_ci	void *rc;
335862306a36Sopenharmony_ci	loff_t idxpos = *pos;
335962306a36Sopenharmony_ci
336062306a36Sopenharmony_ci	rc = neigh_get_idx(seq, &idxpos);
336162306a36Sopenharmony_ci	if (!rc && !(state->flags & NEIGH_SEQ_NEIGH_ONLY))
336262306a36Sopenharmony_ci		rc = pneigh_get_idx(seq, &idxpos);
336362306a36Sopenharmony_ci
336462306a36Sopenharmony_ci	return rc;
336562306a36Sopenharmony_ci}
336662306a36Sopenharmony_ci
336762306a36Sopenharmony_civoid *neigh_seq_start(struct seq_file *seq, loff_t *pos, struct neigh_table *tbl, unsigned int neigh_seq_flags)
336862306a36Sopenharmony_ci	__acquires(tbl->lock)
336962306a36Sopenharmony_ci	__acquires(rcu)
337062306a36Sopenharmony_ci{
337162306a36Sopenharmony_ci	struct neigh_seq_state *state = seq->private;
337262306a36Sopenharmony_ci
337362306a36Sopenharmony_ci	state->tbl = tbl;
337462306a36Sopenharmony_ci	state->bucket = 0;
337562306a36Sopenharmony_ci	state->flags = (neigh_seq_flags & ~NEIGH_SEQ_IS_PNEIGH);
337662306a36Sopenharmony_ci
337762306a36Sopenharmony_ci	rcu_read_lock();
337862306a36Sopenharmony_ci	state->nht = rcu_dereference(tbl->nht);
337962306a36Sopenharmony_ci	read_lock_bh(&tbl->lock);
338062306a36Sopenharmony_ci
338162306a36Sopenharmony_ci	return *pos ? neigh_get_idx_any(seq, pos) : SEQ_START_TOKEN;
338262306a36Sopenharmony_ci}
338362306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_seq_start);
338462306a36Sopenharmony_ci
338562306a36Sopenharmony_civoid *neigh_seq_next(struct seq_file *seq, void *v, loff_t *pos)
338662306a36Sopenharmony_ci{
338762306a36Sopenharmony_ci	struct neigh_seq_state *state;
338862306a36Sopenharmony_ci	void *rc;
338962306a36Sopenharmony_ci
339062306a36Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
339162306a36Sopenharmony_ci		rc = neigh_get_first(seq);
339262306a36Sopenharmony_ci		goto out;
339362306a36Sopenharmony_ci	}
339462306a36Sopenharmony_ci
339562306a36Sopenharmony_ci	state = seq->private;
339662306a36Sopenharmony_ci	if (!(state->flags & NEIGH_SEQ_IS_PNEIGH)) {
339762306a36Sopenharmony_ci		rc = neigh_get_next(seq, v, NULL);
339862306a36Sopenharmony_ci		if (rc)
339962306a36Sopenharmony_ci			goto out;
340062306a36Sopenharmony_ci		if (!(state->flags & NEIGH_SEQ_NEIGH_ONLY))
340162306a36Sopenharmony_ci			rc = pneigh_get_first(seq);
340262306a36Sopenharmony_ci	} else {
340362306a36Sopenharmony_ci		BUG_ON(state->flags & NEIGH_SEQ_NEIGH_ONLY);
340462306a36Sopenharmony_ci		rc = pneigh_get_next(seq, v, NULL);
340562306a36Sopenharmony_ci	}
340662306a36Sopenharmony_ciout:
340762306a36Sopenharmony_ci	++(*pos);
340862306a36Sopenharmony_ci	return rc;
340962306a36Sopenharmony_ci}
341062306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_seq_next);
341162306a36Sopenharmony_ci
341262306a36Sopenharmony_civoid neigh_seq_stop(struct seq_file *seq, void *v)
341362306a36Sopenharmony_ci	__releases(tbl->lock)
341462306a36Sopenharmony_ci	__releases(rcu)
341562306a36Sopenharmony_ci{
341662306a36Sopenharmony_ci	struct neigh_seq_state *state = seq->private;
341762306a36Sopenharmony_ci	struct neigh_table *tbl = state->tbl;
341862306a36Sopenharmony_ci
341962306a36Sopenharmony_ci	read_unlock_bh(&tbl->lock);
342062306a36Sopenharmony_ci	rcu_read_unlock();
342162306a36Sopenharmony_ci}
342262306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_seq_stop);
342362306a36Sopenharmony_ci
342462306a36Sopenharmony_ci/* statistics via seq_file */
342562306a36Sopenharmony_ci
342662306a36Sopenharmony_cistatic void *neigh_stat_seq_start(struct seq_file *seq, loff_t *pos)
342762306a36Sopenharmony_ci{
342862306a36Sopenharmony_ci	struct neigh_table *tbl = pde_data(file_inode(seq->file));
342962306a36Sopenharmony_ci	int cpu;
343062306a36Sopenharmony_ci
343162306a36Sopenharmony_ci	if (*pos == 0)
343262306a36Sopenharmony_ci		return SEQ_START_TOKEN;
343362306a36Sopenharmony_ci
343462306a36Sopenharmony_ci	for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) {
343562306a36Sopenharmony_ci		if (!cpu_possible(cpu))
343662306a36Sopenharmony_ci			continue;
343762306a36Sopenharmony_ci		*pos = cpu+1;
343862306a36Sopenharmony_ci		return per_cpu_ptr(tbl->stats, cpu);
343962306a36Sopenharmony_ci	}
344062306a36Sopenharmony_ci	return NULL;
344162306a36Sopenharmony_ci}
344262306a36Sopenharmony_ci
344362306a36Sopenharmony_cistatic void *neigh_stat_seq_next(struct seq_file *seq, void *v, loff_t *pos)
344462306a36Sopenharmony_ci{
344562306a36Sopenharmony_ci	struct neigh_table *tbl = pde_data(file_inode(seq->file));
344662306a36Sopenharmony_ci	int cpu;
344762306a36Sopenharmony_ci
344862306a36Sopenharmony_ci	for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) {
344962306a36Sopenharmony_ci		if (!cpu_possible(cpu))
345062306a36Sopenharmony_ci			continue;
345162306a36Sopenharmony_ci		*pos = cpu+1;
345262306a36Sopenharmony_ci		return per_cpu_ptr(tbl->stats, cpu);
345362306a36Sopenharmony_ci	}
345462306a36Sopenharmony_ci	(*pos)++;
345562306a36Sopenharmony_ci	return NULL;
345662306a36Sopenharmony_ci}
345762306a36Sopenharmony_ci
345862306a36Sopenharmony_cistatic void neigh_stat_seq_stop(struct seq_file *seq, void *v)
345962306a36Sopenharmony_ci{
346062306a36Sopenharmony_ci
346162306a36Sopenharmony_ci}
346262306a36Sopenharmony_ci
346362306a36Sopenharmony_cistatic int neigh_stat_seq_show(struct seq_file *seq, void *v)
346462306a36Sopenharmony_ci{
346562306a36Sopenharmony_ci	struct neigh_table *tbl = pde_data(file_inode(seq->file));
346662306a36Sopenharmony_ci	struct neigh_statistics *st = v;
346762306a36Sopenharmony_ci
346862306a36Sopenharmony_ci	if (v == SEQ_START_TOKEN) {
346962306a36Sopenharmony_ci		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");
347062306a36Sopenharmony_ci		return 0;
347162306a36Sopenharmony_ci	}
347262306a36Sopenharmony_ci
347362306a36Sopenharmony_ci	seq_printf(seq, "%08x %08lx %08lx %08lx   %08lx %08lx %08lx   "
347462306a36Sopenharmony_ci			"%08lx         %08lx         %08lx         "
347562306a36Sopenharmony_ci			"%08lx       %08lx            %08lx\n",
347662306a36Sopenharmony_ci		   atomic_read(&tbl->entries),
347762306a36Sopenharmony_ci
347862306a36Sopenharmony_ci		   st->allocs,
347962306a36Sopenharmony_ci		   st->destroys,
348062306a36Sopenharmony_ci		   st->hash_grows,
348162306a36Sopenharmony_ci
348262306a36Sopenharmony_ci		   st->lookups,
348362306a36Sopenharmony_ci		   st->hits,
348462306a36Sopenharmony_ci
348562306a36Sopenharmony_ci		   st->res_failed,
348662306a36Sopenharmony_ci
348762306a36Sopenharmony_ci		   st->rcv_probes_mcast,
348862306a36Sopenharmony_ci		   st->rcv_probes_ucast,
348962306a36Sopenharmony_ci
349062306a36Sopenharmony_ci		   st->periodic_gc_runs,
349162306a36Sopenharmony_ci		   st->forced_gc_runs,
349262306a36Sopenharmony_ci		   st->unres_discards,
349362306a36Sopenharmony_ci		   st->table_fulls
349462306a36Sopenharmony_ci		   );
349562306a36Sopenharmony_ci
349662306a36Sopenharmony_ci	return 0;
349762306a36Sopenharmony_ci}
349862306a36Sopenharmony_ci
349962306a36Sopenharmony_cistatic const struct seq_operations neigh_stat_seq_ops = {
350062306a36Sopenharmony_ci	.start	= neigh_stat_seq_start,
350162306a36Sopenharmony_ci	.next	= neigh_stat_seq_next,
350262306a36Sopenharmony_ci	.stop	= neigh_stat_seq_stop,
350362306a36Sopenharmony_ci	.show	= neigh_stat_seq_show,
350462306a36Sopenharmony_ci};
350562306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */
350662306a36Sopenharmony_ci
350762306a36Sopenharmony_cistatic void __neigh_notify(struct neighbour *n, int type, int flags,
350862306a36Sopenharmony_ci			   u32 pid)
350962306a36Sopenharmony_ci{
351062306a36Sopenharmony_ci	struct net *net = dev_net(n->dev);
351162306a36Sopenharmony_ci	struct sk_buff *skb;
351262306a36Sopenharmony_ci	int err = -ENOBUFS;
351362306a36Sopenharmony_ci
351462306a36Sopenharmony_ci	skb = nlmsg_new(neigh_nlmsg_size(), GFP_ATOMIC);
351562306a36Sopenharmony_ci	if (skb == NULL)
351662306a36Sopenharmony_ci		goto errout;
351762306a36Sopenharmony_ci
351862306a36Sopenharmony_ci	err = neigh_fill_info(skb, n, pid, 0, type, flags);
351962306a36Sopenharmony_ci	if (err < 0) {
352062306a36Sopenharmony_ci		/* -EMSGSIZE implies BUG in neigh_nlmsg_size() */
352162306a36Sopenharmony_ci		WARN_ON(err == -EMSGSIZE);
352262306a36Sopenharmony_ci		kfree_skb(skb);
352362306a36Sopenharmony_ci		goto errout;
352462306a36Sopenharmony_ci	}
352562306a36Sopenharmony_ci	rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC);
352662306a36Sopenharmony_ci	return;
352762306a36Sopenharmony_cierrout:
352862306a36Sopenharmony_ci	if (err < 0)
352962306a36Sopenharmony_ci		rtnl_set_sk_err(net, RTNLGRP_NEIGH, err);
353062306a36Sopenharmony_ci}
353162306a36Sopenharmony_ci
353262306a36Sopenharmony_civoid neigh_app_ns(struct neighbour *n)
353362306a36Sopenharmony_ci{
353462306a36Sopenharmony_ci	__neigh_notify(n, RTM_GETNEIGH, NLM_F_REQUEST, 0);
353562306a36Sopenharmony_ci}
353662306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_app_ns);
353762306a36Sopenharmony_ci
353862306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
353962306a36Sopenharmony_cistatic int unres_qlen_max = INT_MAX / SKB_TRUESIZE(ETH_FRAME_LEN);
354062306a36Sopenharmony_ci
354162306a36Sopenharmony_cistatic int proc_unres_qlen(struct ctl_table *ctl, int write,
354262306a36Sopenharmony_ci			   void *buffer, size_t *lenp, loff_t *ppos)
354362306a36Sopenharmony_ci{
354462306a36Sopenharmony_ci	int size, ret;
354562306a36Sopenharmony_ci	struct ctl_table tmp = *ctl;
354662306a36Sopenharmony_ci
354762306a36Sopenharmony_ci	tmp.extra1 = SYSCTL_ZERO;
354862306a36Sopenharmony_ci	tmp.extra2 = &unres_qlen_max;
354962306a36Sopenharmony_ci	tmp.data = &size;
355062306a36Sopenharmony_ci
355162306a36Sopenharmony_ci	size = *(int *)ctl->data / SKB_TRUESIZE(ETH_FRAME_LEN);
355262306a36Sopenharmony_ci	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
355362306a36Sopenharmony_ci
355462306a36Sopenharmony_ci	if (write && !ret)
355562306a36Sopenharmony_ci		*(int *)ctl->data = size * SKB_TRUESIZE(ETH_FRAME_LEN);
355662306a36Sopenharmony_ci	return ret;
355762306a36Sopenharmony_ci}
355862306a36Sopenharmony_ci
355962306a36Sopenharmony_cistatic void neigh_copy_dflt_parms(struct net *net, struct neigh_parms *p,
356062306a36Sopenharmony_ci				  int index)
356162306a36Sopenharmony_ci{
356262306a36Sopenharmony_ci	struct net_device *dev;
356362306a36Sopenharmony_ci	int family = neigh_parms_family(p);
356462306a36Sopenharmony_ci
356562306a36Sopenharmony_ci	rcu_read_lock();
356662306a36Sopenharmony_ci	for_each_netdev_rcu(net, dev) {
356762306a36Sopenharmony_ci		struct neigh_parms *dst_p =
356862306a36Sopenharmony_ci				neigh_get_dev_parms_rcu(dev, family);
356962306a36Sopenharmony_ci
357062306a36Sopenharmony_ci		if (dst_p && !test_bit(index, dst_p->data_state))
357162306a36Sopenharmony_ci			dst_p->data[index] = p->data[index];
357262306a36Sopenharmony_ci	}
357362306a36Sopenharmony_ci	rcu_read_unlock();
357462306a36Sopenharmony_ci}
357562306a36Sopenharmony_ci
357662306a36Sopenharmony_cistatic void neigh_proc_update(struct ctl_table *ctl, int write)
357762306a36Sopenharmony_ci{
357862306a36Sopenharmony_ci	struct net_device *dev = ctl->extra1;
357962306a36Sopenharmony_ci	struct neigh_parms *p = ctl->extra2;
358062306a36Sopenharmony_ci	struct net *net = neigh_parms_net(p);
358162306a36Sopenharmony_ci	int index = (int *) ctl->data - p->data;
358262306a36Sopenharmony_ci
358362306a36Sopenharmony_ci	if (!write)
358462306a36Sopenharmony_ci		return;
358562306a36Sopenharmony_ci
358662306a36Sopenharmony_ci	set_bit(index, p->data_state);
358762306a36Sopenharmony_ci	if (index == NEIGH_VAR_DELAY_PROBE_TIME)
358862306a36Sopenharmony_ci		call_netevent_notifiers(NETEVENT_DELAY_PROBE_TIME_UPDATE, p);
358962306a36Sopenharmony_ci	if (!dev) /* NULL dev means this is default value */
359062306a36Sopenharmony_ci		neigh_copy_dflt_parms(net, p, index);
359162306a36Sopenharmony_ci}
359262306a36Sopenharmony_ci
359362306a36Sopenharmony_cistatic int neigh_proc_dointvec_zero_intmax(struct ctl_table *ctl, int write,
359462306a36Sopenharmony_ci					   void *buffer, size_t *lenp,
359562306a36Sopenharmony_ci					   loff_t *ppos)
359662306a36Sopenharmony_ci{
359762306a36Sopenharmony_ci	struct ctl_table tmp = *ctl;
359862306a36Sopenharmony_ci	int ret;
359962306a36Sopenharmony_ci
360062306a36Sopenharmony_ci	tmp.extra1 = SYSCTL_ZERO;
360162306a36Sopenharmony_ci	tmp.extra2 = SYSCTL_INT_MAX;
360262306a36Sopenharmony_ci
360362306a36Sopenharmony_ci	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
360462306a36Sopenharmony_ci	neigh_proc_update(ctl, write);
360562306a36Sopenharmony_ci	return ret;
360662306a36Sopenharmony_ci}
360762306a36Sopenharmony_ci
360862306a36Sopenharmony_cistatic int neigh_proc_dointvec_ms_jiffies_positive(struct ctl_table *ctl, int write,
360962306a36Sopenharmony_ci						   void *buffer, size_t *lenp, loff_t *ppos)
361062306a36Sopenharmony_ci{
361162306a36Sopenharmony_ci	struct ctl_table tmp = *ctl;
361262306a36Sopenharmony_ci	int ret;
361362306a36Sopenharmony_ci
361462306a36Sopenharmony_ci	int min = msecs_to_jiffies(1);
361562306a36Sopenharmony_ci
361662306a36Sopenharmony_ci	tmp.extra1 = &min;
361762306a36Sopenharmony_ci	tmp.extra2 = NULL;
361862306a36Sopenharmony_ci
361962306a36Sopenharmony_ci	ret = proc_dointvec_ms_jiffies_minmax(&tmp, write, buffer, lenp, ppos);
362062306a36Sopenharmony_ci	neigh_proc_update(ctl, write);
362162306a36Sopenharmony_ci	return ret;
362262306a36Sopenharmony_ci}
362362306a36Sopenharmony_ci
362462306a36Sopenharmony_ciint neigh_proc_dointvec(struct ctl_table *ctl, int write, void *buffer,
362562306a36Sopenharmony_ci			size_t *lenp, loff_t *ppos)
362662306a36Sopenharmony_ci{
362762306a36Sopenharmony_ci	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
362862306a36Sopenharmony_ci
362962306a36Sopenharmony_ci	neigh_proc_update(ctl, write);
363062306a36Sopenharmony_ci	return ret;
363162306a36Sopenharmony_ci}
363262306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_proc_dointvec);
363362306a36Sopenharmony_ci
363462306a36Sopenharmony_ciint neigh_proc_dointvec_jiffies(struct ctl_table *ctl, int write, void *buffer,
363562306a36Sopenharmony_ci				size_t *lenp, loff_t *ppos)
363662306a36Sopenharmony_ci{
363762306a36Sopenharmony_ci	int ret = proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos);
363862306a36Sopenharmony_ci
363962306a36Sopenharmony_ci	neigh_proc_update(ctl, write);
364062306a36Sopenharmony_ci	return ret;
364162306a36Sopenharmony_ci}
364262306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_proc_dointvec_jiffies);
364362306a36Sopenharmony_ci
364462306a36Sopenharmony_cistatic int neigh_proc_dointvec_userhz_jiffies(struct ctl_table *ctl, int write,
364562306a36Sopenharmony_ci					      void *buffer, size_t *lenp,
364662306a36Sopenharmony_ci					      loff_t *ppos)
364762306a36Sopenharmony_ci{
364862306a36Sopenharmony_ci	int ret = proc_dointvec_userhz_jiffies(ctl, write, buffer, lenp, ppos);
364962306a36Sopenharmony_ci
365062306a36Sopenharmony_ci	neigh_proc_update(ctl, write);
365162306a36Sopenharmony_ci	return ret;
365262306a36Sopenharmony_ci}
365362306a36Sopenharmony_ci
365462306a36Sopenharmony_ciint neigh_proc_dointvec_ms_jiffies(struct ctl_table *ctl, int write,
365562306a36Sopenharmony_ci				   void *buffer, size_t *lenp, loff_t *ppos)
365662306a36Sopenharmony_ci{
365762306a36Sopenharmony_ci	int ret = proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos);
365862306a36Sopenharmony_ci
365962306a36Sopenharmony_ci	neigh_proc_update(ctl, write);
366062306a36Sopenharmony_ci	return ret;
366162306a36Sopenharmony_ci}
366262306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_proc_dointvec_ms_jiffies);
366362306a36Sopenharmony_ci
366462306a36Sopenharmony_cistatic int neigh_proc_dointvec_unres_qlen(struct ctl_table *ctl, int write,
366562306a36Sopenharmony_ci					  void *buffer, size_t *lenp,
366662306a36Sopenharmony_ci					  loff_t *ppos)
366762306a36Sopenharmony_ci{
366862306a36Sopenharmony_ci	int ret = proc_unres_qlen(ctl, write, buffer, lenp, ppos);
366962306a36Sopenharmony_ci
367062306a36Sopenharmony_ci	neigh_proc_update(ctl, write);
367162306a36Sopenharmony_ci	return ret;
367262306a36Sopenharmony_ci}
367362306a36Sopenharmony_ci
367462306a36Sopenharmony_cistatic int neigh_proc_base_reachable_time(struct ctl_table *ctl, int write,
367562306a36Sopenharmony_ci					  void *buffer, size_t *lenp,
367662306a36Sopenharmony_ci					  loff_t *ppos)
367762306a36Sopenharmony_ci{
367862306a36Sopenharmony_ci	struct neigh_parms *p = ctl->extra2;
367962306a36Sopenharmony_ci	int ret;
368062306a36Sopenharmony_ci
368162306a36Sopenharmony_ci	if (strcmp(ctl->procname, "base_reachable_time") == 0)
368262306a36Sopenharmony_ci		ret = neigh_proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos);
368362306a36Sopenharmony_ci	else if (strcmp(ctl->procname, "base_reachable_time_ms") == 0)
368462306a36Sopenharmony_ci		ret = neigh_proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos);
368562306a36Sopenharmony_ci	else
368662306a36Sopenharmony_ci		ret = -1;
368762306a36Sopenharmony_ci
368862306a36Sopenharmony_ci	if (write && ret == 0) {
368962306a36Sopenharmony_ci		/* update reachable_time as well, otherwise, the change will
369062306a36Sopenharmony_ci		 * only be effective after the next time neigh_periodic_work
369162306a36Sopenharmony_ci		 * decides to recompute it
369262306a36Sopenharmony_ci		 */
369362306a36Sopenharmony_ci		p->reachable_time =
369462306a36Sopenharmony_ci			neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME));
369562306a36Sopenharmony_ci	}
369662306a36Sopenharmony_ci	return ret;
369762306a36Sopenharmony_ci}
369862306a36Sopenharmony_ci
369962306a36Sopenharmony_ci#define NEIGH_PARMS_DATA_OFFSET(index)	\
370062306a36Sopenharmony_ci	(&((struct neigh_parms *) 0)->data[index])
370162306a36Sopenharmony_ci
370262306a36Sopenharmony_ci#define NEIGH_SYSCTL_ENTRY(attr, data_attr, name, mval, proc) \
370362306a36Sopenharmony_ci	[NEIGH_VAR_ ## attr] = { \
370462306a36Sopenharmony_ci		.procname	= name, \
370562306a36Sopenharmony_ci		.data		= NEIGH_PARMS_DATA_OFFSET(NEIGH_VAR_ ## data_attr), \
370662306a36Sopenharmony_ci		.maxlen		= sizeof(int), \
370762306a36Sopenharmony_ci		.mode		= mval, \
370862306a36Sopenharmony_ci		.proc_handler	= proc, \
370962306a36Sopenharmony_ci	}
371062306a36Sopenharmony_ci
371162306a36Sopenharmony_ci#define NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(attr, name) \
371262306a36Sopenharmony_ci	NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_zero_intmax)
371362306a36Sopenharmony_ci
371462306a36Sopenharmony_ci#define NEIGH_SYSCTL_JIFFIES_ENTRY(attr, name) \
371562306a36Sopenharmony_ci	NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_jiffies)
371662306a36Sopenharmony_ci
371762306a36Sopenharmony_ci#define NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(attr, name) \
371862306a36Sopenharmony_ci	NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_userhz_jiffies)
371962306a36Sopenharmony_ci
372062306a36Sopenharmony_ci#define NEIGH_SYSCTL_MS_JIFFIES_POSITIVE_ENTRY(attr, name) \
372162306a36Sopenharmony_ci	NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_ms_jiffies_positive)
372262306a36Sopenharmony_ci
372362306a36Sopenharmony_ci#define NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(attr, data_attr, name) \
372462306a36Sopenharmony_ci	NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_ms_jiffies)
372562306a36Sopenharmony_ci
372662306a36Sopenharmony_ci#define NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(attr, data_attr, name) \
372762306a36Sopenharmony_ci	NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_unres_qlen)
372862306a36Sopenharmony_ci
372962306a36Sopenharmony_cistatic struct neigh_sysctl_table {
373062306a36Sopenharmony_ci	struct ctl_table_header *sysctl_header;
373162306a36Sopenharmony_ci	struct ctl_table neigh_vars[NEIGH_VAR_MAX + 1];
373262306a36Sopenharmony_ci} neigh_sysctl_template __read_mostly = {
373362306a36Sopenharmony_ci	.neigh_vars = {
373462306a36Sopenharmony_ci		NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(MCAST_PROBES, "mcast_solicit"),
373562306a36Sopenharmony_ci		NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(UCAST_PROBES, "ucast_solicit"),
373662306a36Sopenharmony_ci		NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(APP_PROBES, "app_solicit"),
373762306a36Sopenharmony_ci		NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(MCAST_REPROBES, "mcast_resolicit"),
373862306a36Sopenharmony_ci		NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(RETRANS_TIME, "retrans_time"),
373962306a36Sopenharmony_ci		NEIGH_SYSCTL_JIFFIES_ENTRY(BASE_REACHABLE_TIME, "base_reachable_time"),
374062306a36Sopenharmony_ci		NEIGH_SYSCTL_JIFFIES_ENTRY(DELAY_PROBE_TIME, "delay_first_probe_time"),
374162306a36Sopenharmony_ci		NEIGH_SYSCTL_MS_JIFFIES_POSITIVE_ENTRY(INTERVAL_PROBE_TIME_MS,
374262306a36Sopenharmony_ci						       "interval_probe_time_ms"),
374362306a36Sopenharmony_ci		NEIGH_SYSCTL_JIFFIES_ENTRY(GC_STALETIME, "gc_stale_time"),
374462306a36Sopenharmony_ci		NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(QUEUE_LEN_BYTES, "unres_qlen_bytes"),
374562306a36Sopenharmony_ci		NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(PROXY_QLEN, "proxy_qlen"),
374662306a36Sopenharmony_ci		NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(ANYCAST_DELAY, "anycast_delay"),
374762306a36Sopenharmony_ci		NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(PROXY_DELAY, "proxy_delay"),
374862306a36Sopenharmony_ci		NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(LOCKTIME, "locktime"),
374962306a36Sopenharmony_ci		NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(QUEUE_LEN, QUEUE_LEN_BYTES, "unres_qlen"),
375062306a36Sopenharmony_ci		NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(RETRANS_TIME_MS, RETRANS_TIME, "retrans_time_ms"),
375162306a36Sopenharmony_ci		NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(BASE_REACHABLE_TIME_MS, BASE_REACHABLE_TIME, "base_reachable_time_ms"),
375262306a36Sopenharmony_ci		[NEIGH_VAR_GC_INTERVAL] = {
375362306a36Sopenharmony_ci			.procname	= "gc_interval",
375462306a36Sopenharmony_ci			.maxlen		= sizeof(int),
375562306a36Sopenharmony_ci			.mode		= 0644,
375662306a36Sopenharmony_ci			.proc_handler	= proc_dointvec_jiffies,
375762306a36Sopenharmony_ci		},
375862306a36Sopenharmony_ci		[NEIGH_VAR_GC_THRESH1] = {
375962306a36Sopenharmony_ci			.procname	= "gc_thresh1",
376062306a36Sopenharmony_ci			.maxlen		= sizeof(int),
376162306a36Sopenharmony_ci			.mode		= 0644,
376262306a36Sopenharmony_ci			.extra1		= SYSCTL_ZERO,
376362306a36Sopenharmony_ci			.extra2		= SYSCTL_INT_MAX,
376462306a36Sopenharmony_ci			.proc_handler	= proc_dointvec_minmax,
376562306a36Sopenharmony_ci		},
376662306a36Sopenharmony_ci		[NEIGH_VAR_GC_THRESH2] = {
376762306a36Sopenharmony_ci			.procname	= "gc_thresh2",
376862306a36Sopenharmony_ci			.maxlen		= sizeof(int),
376962306a36Sopenharmony_ci			.mode		= 0644,
377062306a36Sopenharmony_ci			.extra1		= SYSCTL_ZERO,
377162306a36Sopenharmony_ci			.extra2		= SYSCTL_INT_MAX,
377262306a36Sopenharmony_ci			.proc_handler	= proc_dointvec_minmax,
377362306a36Sopenharmony_ci		},
377462306a36Sopenharmony_ci		[NEIGH_VAR_GC_THRESH3] = {
377562306a36Sopenharmony_ci			.procname	= "gc_thresh3",
377662306a36Sopenharmony_ci			.maxlen		= sizeof(int),
377762306a36Sopenharmony_ci			.mode		= 0644,
377862306a36Sopenharmony_ci			.extra1		= SYSCTL_ZERO,
377962306a36Sopenharmony_ci			.extra2		= SYSCTL_INT_MAX,
378062306a36Sopenharmony_ci			.proc_handler	= proc_dointvec_minmax,
378162306a36Sopenharmony_ci		},
378262306a36Sopenharmony_ci		{},
378362306a36Sopenharmony_ci	},
378462306a36Sopenharmony_ci};
378562306a36Sopenharmony_ci
378662306a36Sopenharmony_ciint neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
378762306a36Sopenharmony_ci			  proc_handler *handler)
378862306a36Sopenharmony_ci{
378962306a36Sopenharmony_ci	int i;
379062306a36Sopenharmony_ci	struct neigh_sysctl_table *t;
379162306a36Sopenharmony_ci	const char *dev_name_source;
379262306a36Sopenharmony_ci	char neigh_path[ sizeof("net//neigh/") + IFNAMSIZ + IFNAMSIZ ];
379362306a36Sopenharmony_ci	char *p_name;
379462306a36Sopenharmony_ci	size_t neigh_vars_size;
379562306a36Sopenharmony_ci
379662306a36Sopenharmony_ci	t = kmemdup(&neigh_sysctl_template, sizeof(*t), GFP_KERNEL_ACCOUNT);
379762306a36Sopenharmony_ci	if (!t)
379862306a36Sopenharmony_ci		goto err;
379962306a36Sopenharmony_ci
380062306a36Sopenharmony_ci	for (i = 0; i < NEIGH_VAR_GC_INTERVAL; i++) {
380162306a36Sopenharmony_ci		t->neigh_vars[i].data += (long) p;
380262306a36Sopenharmony_ci		t->neigh_vars[i].extra1 = dev;
380362306a36Sopenharmony_ci		t->neigh_vars[i].extra2 = p;
380462306a36Sopenharmony_ci	}
380562306a36Sopenharmony_ci
380662306a36Sopenharmony_ci	neigh_vars_size = ARRAY_SIZE(t->neigh_vars);
380762306a36Sopenharmony_ci	if (dev) {
380862306a36Sopenharmony_ci		dev_name_source = dev->name;
380962306a36Sopenharmony_ci		/* Terminate the table early */
381062306a36Sopenharmony_ci		memset(&t->neigh_vars[NEIGH_VAR_GC_INTERVAL], 0,
381162306a36Sopenharmony_ci		       sizeof(t->neigh_vars[NEIGH_VAR_GC_INTERVAL]));
381262306a36Sopenharmony_ci		neigh_vars_size = NEIGH_VAR_BASE_REACHABLE_TIME_MS + 1;
381362306a36Sopenharmony_ci	} else {
381462306a36Sopenharmony_ci		struct neigh_table *tbl = p->tbl;
381562306a36Sopenharmony_ci		dev_name_source = "default";
381662306a36Sopenharmony_ci		t->neigh_vars[NEIGH_VAR_GC_INTERVAL].data = &tbl->gc_interval;
381762306a36Sopenharmony_ci		t->neigh_vars[NEIGH_VAR_GC_THRESH1].data = &tbl->gc_thresh1;
381862306a36Sopenharmony_ci		t->neigh_vars[NEIGH_VAR_GC_THRESH2].data = &tbl->gc_thresh2;
381962306a36Sopenharmony_ci		t->neigh_vars[NEIGH_VAR_GC_THRESH3].data = &tbl->gc_thresh3;
382062306a36Sopenharmony_ci	}
382162306a36Sopenharmony_ci
382262306a36Sopenharmony_ci	if (handler) {
382362306a36Sopenharmony_ci		/* RetransTime */
382462306a36Sopenharmony_ci		t->neigh_vars[NEIGH_VAR_RETRANS_TIME].proc_handler = handler;
382562306a36Sopenharmony_ci		/* ReachableTime */
382662306a36Sopenharmony_ci		t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].proc_handler = handler;
382762306a36Sopenharmony_ci		/* RetransTime (in milliseconds)*/
382862306a36Sopenharmony_ci		t->neigh_vars[NEIGH_VAR_RETRANS_TIME_MS].proc_handler = handler;
382962306a36Sopenharmony_ci		/* ReachableTime (in milliseconds) */
383062306a36Sopenharmony_ci		t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].proc_handler = handler;
383162306a36Sopenharmony_ci	} else {
383262306a36Sopenharmony_ci		/* Those handlers will update p->reachable_time after
383362306a36Sopenharmony_ci		 * base_reachable_time(_ms) is set to ensure the new timer starts being
383462306a36Sopenharmony_ci		 * applied after the next neighbour update instead of waiting for
383562306a36Sopenharmony_ci		 * neigh_periodic_work to update its value (can be multiple minutes)
383662306a36Sopenharmony_ci		 * So any handler that replaces them should do this as well
383762306a36Sopenharmony_ci		 */
383862306a36Sopenharmony_ci		/* ReachableTime */
383962306a36Sopenharmony_ci		t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].proc_handler =
384062306a36Sopenharmony_ci			neigh_proc_base_reachable_time;
384162306a36Sopenharmony_ci		/* ReachableTime (in milliseconds) */
384262306a36Sopenharmony_ci		t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].proc_handler =
384362306a36Sopenharmony_ci			neigh_proc_base_reachable_time;
384462306a36Sopenharmony_ci	}
384562306a36Sopenharmony_ci
384662306a36Sopenharmony_ci	switch (neigh_parms_family(p)) {
384762306a36Sopenharmony_ci	case AF_INET:
384862306a36Sopenharmony_ci	      p_name = "ipv4";
384962306a36Sopenharmony_ci	      break;
385062306a36Sopenharmony_ci	case AF_INET6:
385162306a36Sopenharmony_ci	      p_name = "ipv6";
385262306a36Sopenharmony_ci	      break;
385362306a36Sopenharmony_ci	default:
385462306a36Sopenharmony_ci	      BUG();
385562306a36Sopenharmony_ci	}
385662306a36Sopenharmony_ci
385762306a36Sopenharmony_ci	snprintf(neigh_path, sizeof(neigh_path), "net/%s/neigh/%s",
385862306a36Sopenharmony_ci		p_name, dev_name_source);
385962306a36Sopenharmony_ci	t->sysctl_header = register_net_sysctl_sz(neigh_parms_net(p),
386062306a36Sopenharmony_ci						  neigh_path, t->neigh_vars,
386162306a36Sopenharmony_ci						  neigh_vars_size);
386262306a36Sopenharmony_ci	if (!t->sysctl_header)
386362306a36Sopenharmony_ci		goto free;
386462306a36Sopenharmony_ci
386562306a36Sopenharmony_ci	p->sysctl_table = t;
386662306a36Sopenharmony_ci	return 0;
386762306a36Sopenharmony_ci
386862306a36Sopenharmony_cifree:
386962306a36Sopenharmony_ci	kfree(t);
387062306a36Sopenharmony_cierr:
387162306a36Sopenharmony_ci	return -ENOBUFS;
387262306a36Sopenharmony_ci}
387362306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_sysctl_register);
387462306a36Sopenharmony_ci
387562306a36Sopenharmony_civoid neigh_sysctl_unregister(struct neigh_parms *p)
387662306a36Sopenharmony_ci{
387762306a36Sopenharmony_ci	if (p->sysctl_table) {
387862306a36Sopenharmony_ci		struct neigh_sysctl_table *t = p->sysctl_table;
387962306a36Sopenharmony_ci		p->sysctl_table = NULL;
388062306a36Sopenharmony_ci		unregister_net_sysctl_table(t->sysctl_header);
388162306a36Sopenharmony_ci		kfree(t);
388262306a36Sopenharmony_ci	}
388362306a36Sopenharmony_ci}
388462306a36Sopenharmony_ciEXPORT_SYMBOL(neigh_sysctl_unregister);
388562306a36Sopenharmony_ci
388662306a36Sopenharmony_ci#endif	/* CONFIG_SYSCTL */
388762306a36Sopenharmony_ci
388862306a36Sopenharmony_cistatic int __init neigh_init(void)
388962306a36Sopenharmony_ci{
389062306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL, 0);
389162306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL, 0);
389262306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_GETNEIGH, neigh_get, neigh_dump_info, 0);
389362306a36Sopenharmony_ci
389462306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info,
389562306a36Sopenharmony_ci		      0);
389662306a36Sopenharmony_ci	rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL, 0);
389762306a36Sopenharmony_ci
389862306a36Sopenharmony_ci	return 0;
389962306a36Sopenharmony_ci}
390062306a36Sopenharmony_ci
390162306a36Sopenharmony_cisubsys_initcall(neigh_init);
3902