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, ¬ify, &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, ¬ify); 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