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