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 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 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 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 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 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 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 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 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 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 2521da177e4SLinus Torvalds static int neigh_forced_gc(struct neigh_table *tbl) 2531da177e4SLinus Torvalds { 25458956317SDavid Ahern int max_clean = atomic_read(&tbl->gc_entries) - tbl->gc_thresh2; 25558956317SDavid Ahern unsigned long tref = jiffies - 5 * HZ; 25658956317SDavid Ahern struct neighbour *n, *tmp; 2571da177e4SLinus Torvalds int shrunk = 0; 2581da177e4SLinus Torvalds 2591da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, forced_gc_runs); 2601da177e4SLinus Torvalds 2611da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 2621da177e4SLinus Torvalds 26358956317SDavid Ahern list_for_each_entry_safe(n, tmp, &tbl->gc_list, gc_list) { 26458956317SDavid Ahern if (refcount_read(&n->refcnt) == 1) { 26558956317SDavid Ahern bool remove = false; 26658956317SDavid Ahern 26758956317SDavid Ahern write_lock(&n->lock); 268758a7f0bSDavid Ahern if ((n->nud_state == NUD_FAILED) || 2697a6b1ab7SDavid Ahern (n->nud_state == NUD_NOARP) || 2708cf8821eSJeff Dike (tbl->is_multicast && 2718cf8821eSJeff Dike tbl->is_multicast(n->primary_key)) || 272c1d2ecdfSJulian Anastasov !time_in_range(n->updated, tref, jiffies)) 27358956317SDavid Ahern remove = true; 27458956317SDavid Ahern write_unlock(&n->lock); 27558956317SDavid Ahern 27658956317SDavid Ahern if (remove && neigh_remove_one(n, tbl)) 27758956317SDavid Ahern shrunk++; 27858956317SDavid Ahern if (shrunk >= max_clean) 27958956317SDavid Ahern break; 2801da177e4SLinus Torvalds } 2811da177e4SLinus Torvalds } 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds tbl->last_flush = jiffies; 2841da177e4SLinus Torvalds 2851da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds return shrunk; 2881da177e4SLinus Torvalds } 2891da177e4SLinus Torvalds 290a43d8994SPavel Emelyanov static void neigh_add_timer(struct neighbour *n, unsigned long when) 291a43d8994SPavel Emelyanov { 292c1d2ecdfSJulian Anastasov /* Use safe distance from the jiffies - LONG_MAX point while timer 293c1d2ecdfSJulian Anastasov * is running in DELAY/PROBE state but still show to user space 294c1d2ecdfSJulian Anastasov * large times in the past. 295c1d2ecdfSJulian Anastasov */ 296c1d2ecdfSJulian Anastasov unsigned long mint = jiffies - (LONG_MAX - 86400 * HZ); 297c1d2ecdfSJulian Anastasov 298a43d8994SPavel Emelyanov neigh_hold(n); 299c1d2ecdfSJulian Anastasov if (!time_in_range(n->confirmed, mint, jiffies)) 300c1d2ecdfSJulian Anastasov n->confirmed = mint; 301c1d2ecdfSJulian Anastasov if (time_before(n->used, n->confirmed)) 302c1d2ecdfSJulian Anastasov n->used = n->confirmed; 303a43d8994SPavel Emelyanov if (unlikely(mod_timer(&n->timer, when))) { 304a43d8994SPavel Emelyanov printk("NEIGH: BUG, double timer add, state is %x\n", 305a43d8994SPavel Emelyanov n->nud_state); 306a43d8994SPavel Emelyanov dump_stack(); 307a43d8994SPavel Emelyanov } 308a43d8994SPavel Emelyanov } 309a43d8994SPavel Emelyanov 3101da177e4SLinus Torvalds static int neigh_del_timer(struct neighbour *n) 3111da177e4SLinus Torvalds { 3121da177e4SLinus Torvalds if ((n->nud_state & NUD_IN_TIMER) && 3131da177e4SLinus Torvalds del_timer(&n->timer)) { 3141da177e4SLinus Torvalds neigh_release(n); 3151da177e4SLinus Torvalds return 1; 3161da177e4SLinus Torvalds } 3171da177e4SLinus Torvalds return 0; 3181da177e4SLinus Torvalds } 3191da177e4SLinus Torvalds 3208207f253SThomas Zeitlhofer static struct neigh_parms *neigh_get_dev_parms_rcu(struct net_device *dev, 3218207f253SThomas Zeitlhofer int family) 3228207f253SThomas Zeitlhofer { 3238207f253SThomas Zeitlhofer switch (family) { 3248207f253SThomas Zeitlhofer case AF_INET: 3258207f253SThomas Zeitlhofer return __in_dev_arp_parms_get_rcu(dev); 3268207f253SThomas Zeitlhofer case AF_INET6: 3278207f253SThomas Zeitlhofer return __in6_dev_nd_parms_get_rcu(dev); 3288207f253SThomas Zeitlhofer } 3298207f253SThomas Zeitlhofer return NULL; 3308207f253SThomas Zeitlhofer } 3318207f253SThomas Zeitlhofer 3328207f253SThomas Zeitlhofer static void neigh_parms_qlen_dec(struct net_device *dev, int family) 3338207f253SThomas Zeitlhofer { 3348207f253SThomas Zeitlhofer struct neigh_parms *p; 3358207f253SThomas Zeitlhofer 3368207f253SThomas Zeitlhofer rcu_read_lock(); 3378207f253SThomas Zeitlhofer p = neigh_get_dev_parms_rcu(dev, family); 3388207f253SThomas Zeitlhofer if (p) 3398207f253SThomas Zeitlhofer p->qlen--; 3408207f253SThomas Zeitlhofer rcu_read_unlock(); 3418207f253SThomas Zeitlhofer } 3428207f253SThomas Zeitlhofer 3438207f253SThomas Zeitlhofer static void pneigh_queue_purge(struct sk_buff_head *list, struct net *net, 3448207f253SThomas Zeitlhofer int family) 3451da177e4SLinus Torvalds { 346d5485d9dSYang Yingliang struct sk_buff_head tmp; 34766ba215cSDenis V. Lunev unsigned long flags; 3481da177e4SLinus Torvalds struct sk_buff *skb; 3491da177e4SLinus Torvalds 350d5485d9dSYang Yingliang skb_queue_head_init(&tmp); 35166ba215cSDenis V. Lunev spin_lock_irqsave(&list->lock, flags); 35266ba215cSDenis V. Lunev skb = skb_peek(list); 35366ba215cSDenis V. Lunev while (skb != NULL) { 35466ba215cSDenis V. Lunev struct sk_buff *skb_next = skb_peek_next(skb, list); 3550ff4eb3dSAlexander Mikhalitsyn struct net_device *dev = skb->dev; 356d5485d9dSYang Yingliang 3570ff4eb3dSAlexander Mikhalitsyn if (net == NULL || net_eq(dev_net(dev), net)) { 3588207f253SThomas Zeitlhofer neigh_parms_qlen_dec(dev, family); 35966ba215cSDenis V. Lunev __skb_unlink(skb, list); 360d5485d9dSYang Yingliang __skb_queue_tail(&tmp, skb); 3611da177e4SLinus Torvalds } 36266ba215cSDenis V. Lunev skb = skb_next; 36366ba215cSDenis V. Lunev } 36466ba215cSDenis V. Lunev spin_unlock_irqrestore(&list->lock, flags); 365d5485d9dSYang Yingliang 366d5485d9dSYang Yingliang while ((skb = __skb_dequeue(&tmp))) { 367d5485d9dSYang Yingliang dev_put(skb->dev); 368d5485d9dSYang Yingliang kfree_skb(skb); 369d5485d9dSYang Yingliang } 3701da177e4SLinus Torvalds } 3711da177e4SLinus Torvalds 372859bd2efSDavid Ahern static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev, 373859bd2efSDavid Ahern bool skip_perm) 3741da177e4SLinus Torvalds { 3751da177e4SLinus Torvalds int i; 376d6bf7817SEric Dumazet struct neigh_hash_table *nht; 3771da177e4SLinus Torvalds 378d6bf7817SEric Dumazet nht = rcu_dereference_protected(tbl->nht, 379d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 380d6bf7817SEric Dumazet 381cd089336SDavid S. Miller for (i = 0; i < (1 << nht->hash_shift); i++) { 382767e97e1SEric Dumazet struct neighbour *n; 383767e97e1SEric Dumazet struct neighbour __rcu **np = &nht->hash_buckets[i]; 3841da177e4SLinus Torvalds 385767e97e1SEric Dumazet while ((n = rcu_dereference_protected(*np, 386767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))) != NULL) { 3871da177e4SLinus Torvalds if (dev && n->dev != dev) { 3881da177e4SLinus Torvalds np = &n->next; 3891da177e4SLinus Torvalds continue; 3901da177e4SLinus Torvalds } 391859bd2efSDavid Ahern if (skip_perm && n->nud_state & NUD_PERMANENT) { 392859bd2efSDavid Ahern np = &n->next; 393859bd2efSDavid Ahern continue; 394859bd2efSDavid Ahern } 395767e97e1SEric Dumazet rcu_assign_pointer(*np, 396767e97e1SEric Dumazet rcu_dereference_protected(n->next, 397767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))); 3981da177e4SLinus Torvalds write_lock(&n->lock); 3991da177e4SLinus Torvalds neigh_del_timer(n); 40058956317SDavid Ahern neigh_mark_dead(n); 4019f237430SReshetova, Elena if (refcount_read(&n->refcnt) != 1) { 4021da177e4SLinus Torvalds /* The most unpleasant situation. 4031da177e4SLinus Torvalds We must destroy neighbour entry, 4041da177e4SLinus Torvalds but someone still uses it. 4051da177e4SLinus Torvalds 4061da177e4SLinus Torvalds The destroy will be delayed until 4071da177e4SLinus Torvalds the last user releases us, but 4081da177e4SLinus Torvalds we must kill timers etc. and move 4091da177e4SLinus Torvalds it to safe state. 4101da177e4SLinus Torvalds */ 411c9ab4d85SEric Dumazet __skb_queue_purge(&n->arp_queue); 4128b5c171bSEric Dumazet n->arp_queue_len_bytes = 0; 4131da177e4SLinus Torvalds n->output = neigh_blackhole; 4141da177e4SLinus Torvalds if (n->nud_state & NUD_VALID) 4151da177e4SLinus Torvalds n->nud_state = NUD_NOARP; 4161da177e4SLinus Torvalds else 4171da177e4SLinus Torvalds n->nud_state = NUD_NONE; 418d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is stray\n", n); 4191da177e4SLinus Torvalds } 4201da177e4SLinus Torvalds write_unlock(&n->lock); 4214f494554SThomas Graf neigh_cleanup_and_release(n); 4221da177e4SLinus Torvalds } 4231da177e4SLinus Torvalds } 42449636bb1SHerbert Xu } 4251da177e4SLinus Torvalds 42649636bb1SHerbert Xu void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev) 42749636bb1SHerbert Xu { 42849636bb1SHerbert Xu write_lock_bh(&tbl->lock); 429859bd2efSDavid Ahern neigh_flush_dev(tbl, dev, false); 43049636bb1SHerbert Xu write_unlock_bh(&tbl->lock); 43149636bb1SHerbert Xu } 4320a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_changeaddr); 43349636bb1SHerbert Xu 434859bd2efSDavid Ahern static int __neigh_ifdown(struct neigh_table *tbl, struct net_device *dev, 435859bd2efSDavid Ahern bool skip_perm) 43649636bb1SHerbert Xu { 43749636bb1SHerbert Xu write_lock_bh(&tbl->lock); 438859bd2efSDavid Ahern neigh_flush_dev(tbl, dev, skip_perm); 43953b76cdfSWolfgang Bumiller pneigh_ifdown_and_unlock(tbl, dev); 4408207f253SThomas Zeitlhofer pneigh_queue_purge(&tbl->proxy_queue, dev ? dev_net(dev) : NULL, 4418207f253SThomas Zeitlhofer tbl->family); 44266ba215cSDenis V. Lunev if (skb_queue_empty_lockless(&tbl->proxy_queue)) 4431da177e4SLinus Torvalds del_timer_sync(&tbl->proxy_timer); 4441da177e4SLinus Torvalds return 0; 4451da177e4SLinus Torvalds } 446859bd2efSDavid Ahern 447859bd2efSDavid Ahern int neigh_carrier_down(struct neigh_table *tbl, struct net_device *dev) 448859bd2efSDavid Ahern { 449859bd2efSDavid Ahern __neigh_ifdown(tbl, dev, true); 450859bd2efSDavid Ahern return 0; 451859bd2efSDavid Ahern } 452859bd2efSDavid Ahern EXPORT_SYMBOL(neigh_carrier_down); 453859bd2efSDavid Ahern 454859bd2efSDavid Ahern int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev) 455859bd2efSDavid Ahern { 456859bd2efSDavid Ahern __neigh_ifdown(tbl, dev, false); 457859bd2efSDavid Ahern return 0; 458859bd2efSDavid Ahern } 4590a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_ifdown); 4601da177e4SLinus Torvalds 46158956317SDavid Ahern static struct neighbour *neigh_alloc(struct neigh_table *tbl, 46258956317SDavid Ahern struct net_device *dev, 4632c611ad9SRoopa Prabhu u32 flags, bool exempt_from_gc) 4641da177e4SLinus Torvalds { 4651da177e4SLinus Torvalds struct neighbour *n = NULL; 4661da177e4SLinus Torvalds unsigned long now = jiffies; 4671da177e4SLinus Torvalds int entries; 4681da177e4SLinus Torvalds 469e997f8a2SDavid Ahern if (exempt_from_gc) 47058956317SDavid Ahern goto do_alloc; 47158956317SDavid Ahern 47258956317SDavid Ahern entries = atomic_inc_return(&tbl->gc_entries) - 1; 4731da177e4SLinus Torvalds if (entries >= tbl->gc_thresh3 || 4741da177e4SLinus Torvalds (entries >= tbl->gc_thresh2 && 4751da177e4SLinus Torvalds time_after(now, tbl->last_flush + 5 * HZ))) { 4761da177e4SLinus Torvalds if (!neigh_forced_gc(tbl) && 477fb811395SRick Jones entries >= tbl->gc_thresh3) { 478fb811395SRick Jones net_info_ratelimited("%s: neighbor table overflow!\n", 479fb811395SRick Jones tbl->id); 480fb811395SRick Jones NEIGH_CACHE_STAT_INC(tbl, table_fulls); 4811da177e4SLinus Torvalds goto out_entries; 4821da177e4SLinus Torvalds } 483fb811395SRick Jones } 4841da177e4SLinus Torvalds 48558956317SDavid Ahern do_alloc: 48608433effSYOSHIFUJI Hideaki / 吉藤英明 n = kzalloc(tbl->entry_size + dev->neigh_priv_len, GFP_ATOMIC); 4871da177e4SLinus Torvalds if (!n) 4881da177e4SLinus Torvalds goto out_entries; 4891da177e4SLinus Torvalds 490c9ab4d85SEric Dumazet __skb_queue_head_init(&n->arp_queue); 4911da177e4SLinus Torvalds rwlock_init(&n->lock); 4920ed8ddf4SEric Dumazet seqlock_init(&n->ha_lock); 4931da177e4SLinus Torvalds n->updated = n->used = now; 4941da177e4SLinus Torvalds n->nud_state = NUD_NONE; 4951da177e4SLinus Torvalds n->output = neigh_blackhole; 496e4400bbfSDaniel Borkmann n->flags = flags; 497f6b72b62SDavid S. Miller seqlock_init(&n->hh.hh_lock); 4981da177e4SLinus Torvalds n->parms = neigh_parms_clone(&tbl->parms); 499e99e88a9SKees Cook timer_setup(&n->timer, neigh_timer_handler, 0); 5001da177e4SLinus Torvalds 5011da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, allocs); 5021da177e4SLinus Torvalds n->tbl = tbl; 5039f237430SReshetova, Elena refcount_set(&n->refcnt, 1); 5041da177e4SLinus Torvalds n->dead = 1; 50558956317SDavid Ahern INIT_LIST_HEAD(&n->gc_list); 5067482e384SDaniel Borkmann INIT_LIST_HEAD(&n->managed_list); 50758956317SDavid Ahern 50858956317SDavid Ahern atomic_inc(&tbl->entries); 5091da177e4SLinus Torvalds out: 5101da177e4SLinus Torvalds return n; 5111da177e4SLinus Torvalds 5121da177e4SLinus Torvalds out_entries: 513e997f8a2SDavid Ahern if (!exempt_from_gc) 51458956317SDavid Ahern atomic_dec(&tbl->gc_entries); 5151da177e4SLinus Torvalds goto out; 5161da177e4SLinus Torvalds } 5171da177e4SLinus Torvalds 5182c2aba6cSDavid S. Miller static void neigh_get_hash_rnd(u32 *x) 5192c2aba6cSDavid S. Miller { 520b3d0f789SJason A. Donenfeld *x = get_random_u32() | 1; 5212c2aba6cSDavid S. Miller } 5222c2aba6cSDavid S. Miller 523cd089336SDavid S. Miller static struct neigh_hash_table *neigh_hash_alloc(unsigned int shift) 5241da177e4SLinus Torvalds { 525cd089336SDavid S. Miller size_t size = (1 << shift) * sizeof(struct neighbour *); 526d6bf7817SEric Dumazet struct neigh_hash_table *ret; 5276193d2beSEric Dumazet struct neighbour __rcu **buckets; 5282c2aba6cSDavid S. Miller int i; 5291da177e4SLinus Torvalds 530d6bf7817SEric Dumazet ret = kmalloc(sizeof(*ret), GFP_ATOMIC); 531d6bf7817SEric Dumazet if (!ret) 532d6bf7817SEric Dumazet return NULL; 53385704cb8SKonstantin Khlebnikov if (size <= PAGE_SIZE) { 534d6bf7817SEric Dumazet buckets = kzalloc(size, GFP_ATOMIC); 53585704cb8SKonstantin Khlebnikov } else { 5366193d2beSEric Dumazet buckets = (struct neighbour __rcu **) 537d6bf7817SEric Dumazet __get_free_pages(GFP_ATOMIC | __GFP_ZERO, 538d6bf7817SEric Dumazet get_order(size)); 53901b833abSKonstantin Khlebnikov kmemleak_alloc(buckets, size, 1, GFP_ATOMIC); 54085704cb8SKonstantin Khlebnikov } 541d6bf7817SEric Dumazet if (!buckets) { 542d6bf7817SEric Dumazet kfree(ret); 543d6bf7817SEric Dumazet return NULL; 5441da177e4SLinus Torvalds } 5456193d2beSEric Dumazet ret->hash_buckets = buckets; 546cd089336SDavid S. Miller ret->hash_shift = shift; 5472c2aba6cSDavid S. Miller for (i = 0; i < NEIGH_NUM_HASH_RND; i++) 5482c2aba6cSDavid S. Miller neigh_get_hash_rnd(&ret->hash_rnd[i]); 5491da177e4SLinus Torvalds return ret; 5501da177e4SLinus Torvalds } 5511da177e4SLinus Torvalds 552d6bf7817SEric Dumazet static void neigh_hash_free_rcu(struct rcu_head *head) 5531da177e4SLinus Torvalds { 554d6bf7817SEric Dumazet struct neigh_hash_table *nht = container_of(head, 555d6bf7817SEric Dumazet struct neigh_hash_table, 556d6bf7817SEric Dumazet rcu); 557cd089336SDavid S. Miller size_t size = (1 << nht->hash_shift) * sizeof(struct neighbour *); 5586193d2beSEric Dumazet struct neighbour __rcu **buckets = nht->hash_buckets; 5591da177e4SLinus Torvalds 56085704cb8SKonstantin Khlebnikov if (size <= PAGE_SIZE) { 561d6bf7817SEric Dumazet kfree(buckets); 56285704cb8SKonstantin Khlebnikov } else { 56385704cb8SKonstantin Khlebnikov kmemleak_free(buckets); 564d6bf7817SEric Dumazet free_pages((unsigned long)buckets, get_order(size)); 56585704cb8SKonstantin Khlebnikov } 566d6bf7817SEric Dumazet kfree(nht); 5671da177e4SLinus Torvalds } 5681da177e4SLinus Torvalds 569d6bf7817SEric Dumazet static struct neigh_hash_table *neigh_hash_grow(struct neigh_table *tbl, 570cd089336SDavid S. Miller unsigned long new_shift) 5711da177e4SLinus Torvalds { 572d6bf7817SEric Dumazet unsigned int i, hash; 573d6bf7817SEric Dumazet struct neigh_hash_table *new_nht, *old_nht; 5741da177e4SLinus Torvalds 5751da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, hash_grows); 5761da177e4SLinus Torvalds 577d6bf7817SEric Dumazet old_nht = rcu_dereference_protected(tbl->nht, 578d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 579cd089336SDavid S. Miller new_nht = neigh_hash_alloc(new_shift); 580d6bf7817SEric Dumazet if (!new_nht) 581d6bf7817SEric Dumazet return old_nht; 5821da177e4SLinus Torvalds 583cd089336SDavid S. Miller for (i = 0; i < (1 << old_nht->hash_shift); i++) { 5841da177e4SLinus Torvalds struct neighbour *n, *next; 5851da177e4SLinus Torvalds 586767e97e1SEric Dumazet for (n = rcu_dereference_protected(old_nht->hash_buckets[i], 587767e97e1SEric Dumazet lockdep_is_held(&tbl->lock)); 588d6bf7817SEric Dumazet n != NULL; 589d6bf7817SEric Dumazet n = next) { 590d6bf7817SEric Dumazet hash = tbl->hash(n->primary_key, n->dev, 591d6bf7817SEric Dumazet new_nht->hash_rnd); 5921da177e4SLinus Torvalds 593cd089336SDavid S. Miller hash >>= (32 - new_nht->hash_shift); 594767e97e1SEric Dumazet next = rcu_dereference_protected(n->next, 595767e97e1SEric Dumazet lockdep_is_held(&tbl->lock)); 5961da177e4SLinus Torvalds 597767e97e1SEric Dumazet rcu_assign_pointer(n->next, 598767e97e1SEric Dumazet rcu_dereference_protected( 599767e97e1SEric Dumazet new_nht->hash_buckets[hash], 600767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))); 601767e97e1SEric Dumazet rcu_assign_pointer(new_nht->hash_buckets[hash], n); 6021da177e4SLinus Torvalds } 6031da177e4SLinus Torvalds } 6041da177e4SLinus Torvalds 605d6bf7817SEric Dumazet rcu_assign_pointer(tbl->nht, new_nht); 606d6bf7817SEric Dumazet call_rcu(&old_nht->rcu, neigh_hash_free_rcu); 607d6bf7817SEric Dumazet return new_nht; 6081da177e4SLinus Torvalds } 6091da177e4SLinus Torvalds 6101da177e4SLinus Torvalds struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey, 6111da177e4SLinus Torvalds struct net_device *dev) 6121da177e4SLinus Torvalds { 6131da177e4SLinus Torvalds struct neighbour *n; 6141da177e4SLinus Torvalds 6151da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, lookups); 6161da177e4SLinus Torvalds 617d6bf7817SEric Dumazet rcu_read_lock_bh(); 61860395a20SEric W. Biederman n = __neigh_lookup_noref(tbl, pkey, dev); 61960395a20SEric W. Biederman if (n) { 6209f237430SReshetova, Elena if (!refcount_inc_not_zero(&n->refcnt)) 621767e97e1SEric Dumazet n = NULL; 6221da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, hits); 6231da177e4SLinus Torvalds } 624767e97e1SEric Dumazet 625d6bf7817SEric Dumazet rcu_read_unlock_bh(); 6261da177e4SLinus Torvalds return n; 6271da177e4SLinus Torvalds } 6280a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_lookup); 6291da177e4SLinus Torvalds 630e4400bbfSDaniel Borkmann static struct neighbour * 631e4400bbfSDaniel Borkmann ___neigh_create(struct neigh_table *tbl, const void *pkey, 6322c611ad9SRoopa Prabhu struct net_device *dev, u32 flags, 633e997f8a2SDavid Ahern bool exempt_from_gc, bool want_ref) 6341da177e4SLinus Torvalds { 635e4400bbfSDaniel Borkmann u32 hash_val, key_len = tbl->key_len; 636e4400bbfSDaniel Borkmann struct neighbour *n1, *rc, *n; 637d6bf7817SEric Dumazet struct neigh_hash_table *nht; 638e4400bbfSDaniel Borkmann int error; 6391da177e4SLinus Torvalds 640e4400bbfSDaniel Borkmann n = neigh_alloc(tbl, dev, flags, exempt_from_gc); 641fc651001SDavid Ahern trace_neigh_create(tbl, dev, pkey, n, exempt_from_gc); 6421da177e4SLinus Torvalds if (!n) { 6431da177e4SLinus Torvalds rc = ERR_PTR(-ENOBUFS); 6441da177e4SLinus Torvalds goto out; 6451da177e4SLinus Torvalds } 6461da177e4SLinus Torvalds 6471da177e4SLinus Torvalds memcpy(n->primary_key, pkey, key_len); 6481da177e4SLinus Torvalds n->dev = dev; 649d62607c3SJakub Kicinski netdev_hold(dev, &n->dev_tracker, GFP_ATOMIC); 6501da177e4SLinus Torvalds 6511da177e4SLinus Torvalds /* Protocol specific setup. */ 6521da177e4SLinus Torvalds if (tbl->constructor && (error = tbl->constructor(n)) < 0) { 6531da177e4SLinus Torvalds rc = ERR_PTR(error); 6541da177e4SLinus Torvalds goto out_neigh_release; 6551da177e4SLinus Torvalds } 6561da177e4SLinus Torvalds 657da6a8fa0SDavid Miller if (dev->netdev_ops->ndo_neigh_construct) { 658503eebc2SJiri Pirko error = dev->netdev_ops->ndo_neigh_construct(dev, n); 659da6a8fa0SDavid Miller if (error < 0) { 660da6a8fa0SDavid Miller rc = ERR_PTR(error); 661da6a8fa0SDavid Miller goto out_neigh_release; 662da6a8fa0SDavid Miller } 663da6a8fa0SDavid Miller } 664da6a8fa0SDavid Miller 665447f2191SDavid S. Miller /* Device specific setup. */ 666447f2191SDavid S. Miller if (n->parms->neigh_setup && 667447f2191SDavid S. Miller (error = n->parms->neigh_setup(n)) < 0) { 668447f2191SDavid S. Miller rc = ERR_PTR(error); 669447f2191SDavid S. Miller goto out_neigh_release; 670447f2191SDavid S. Miller } 671447f2191SDavid S. Miller 6721f9248e5SJiri Pirko n->confirmed = jiffies - (NEIGH_VAR(n->parms, BASE_REACHABLE_TIME) << 1); 6731da177e4SLinus Torvalds 6741da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 675d6bf7817SEric Dumazet nht = rcu_dereference_protected(tbl->nht, 676d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 6771da177e4SLinus Torvalds 678cd089336SDavid S. Miller if (atomic_read(&tbl->entries) > (1 << nht->hash_shift)) 679cd089336SDavid S. Miller nht = neigh_hash_grow(tbl, nht->hash_shift + 1); 6801da177e4SLinus Torvalds 681096b9854SJim Westfall hash_val = tbl->hash(n->primary_key, dev, nht->hash_rnd) >> (32 - nht->hash_shift); 6821da177e4SLinus Torvalds 6831da177e4SLinus Torvalds if (n->parms->dead) { 6841da177e4SLinus Torvalds rc = ERR_PTR(-EINVAL); 6851da177e4SLinus Torvalds goto out_tbl_unlock; 6861da177e4SLinus Torvalds } 6871da177e4SLinus Torvalds 688767e97e1SEric Dumazet for (n1 = rcu_dereference_protected(nht->hash_buckets[hash_val], 689767e97e1SEric Dumazet lockdep_is_held(&tbl->lock)); 690767e97e1SEric Dumazet n1 != NULL; 691767e97e1SEric Dumazet n1 = rcu_dereference_protected(n1->next, 692767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))) { 693096b9854SJim Westfall if (dev == n1->dev && !memcmp(n1->primary_key, n->primary_key, key_len)) { 694a263b309SDavid S. Miller if (want_ref) 6951da177e4SLinus Torvalds neigh_hold(n1); 6961da177e4SLinus Torvalds rc = n1; 6971da177e4SLinus Torvalds goto out_tbl_unlock; 6981da177e4SLinus Torvalds } 6991da177e4SLinus Torvalds } 7001da177e4SLinus Torvalds 7011da177e4SLinus Torvalds n->dead = 0; 702e997f8a2SDavid Ahern if (!exempt_from_gc) 7038cc196d6SDavid Ahern list_add_tail(&n->gc_list, &n->tbl->gc_list); 7047482e384SDaniel Borkmann if (n->flags & NTF_MANAGED) 7057482e384SDaniel Borkmann list_add_tail(&n->managed_list, &n->tbl->managed_list); 706a263b309SDavid S. Miller if (want_ref) 7071da177e4SLinus Torvalds neigh_hold(n); 708767e97e1SEric Dumazet rcu_assign_pointer(n->next, 709767e97e1SEric Dumazet rcu_dereference_protected(nht->hash_buckets[hash_val], 710767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))); 711767e97e1SEric Dumazet rcu_assign_pointer(nht->hash_buckets[hash_val], n); 7121da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 713d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is created\n", n); 7141da177e4SLinus Torvalds rc = n; 7151da177e4SLinus Torvalds out: 7161da177e4SLinus Torvalds return rc; 7171da177e4SLinus Torvalds out_tbl_unlock: 7181da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 7191da177e4SLinus Torvalds out_neigh_release: 72064c6f4bbSDavid Ahern if (!exempt_from_gc) 72164c6f4bbSDavid Ahern atomic_dec(&tbl->gc_entries); 7221da177e4SLinus Torvalds neigh_release(n); 7231da177e4SLinus Torvalds goto out; 7241da177e4SLinus Torvalds } 72558956317SDavid Ahern 72658956317SDavid Ahern struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey, 72758956317SDavid Ahern struct net_device *dev, bool want_ref) 72858956317SDavid Ahern { 729e4400bbfSDaniel Borkmann return ___neigh_create(tbl, pkey, dev, 0, false, want_ref); 73058956317SDavid Ahern } 731a263b309SDavid S. Miller EXPORT_SYMBOL(__neigh_create); 7321da177e4SLinus Torvalds 73301ccdf12SAlexey Dobriyan static u32 pneigh_hash(const void *pkey, unsigned int key_len) 734fa86d322SPavel Emelyanov { 735fa86d322SPavel Emelyanov u32 hash_val = *(u32 *)(pkey + key_len - 4); 736fa86d322SPavel Emelyanov hash_val ^= (hash_val >> 16); 737fa86d322SPavel Emelyanov hash_val ^= hash_val >> 8; 738fa86d322SPavel Emelyanov hash_val ^= hash_val >> 4; 739fa86d322SPavel Emelyanov hash_val &= PNEIGH_HASHMASK; 740be01d655SYOSHIFUJI Hideaki return hash_val; 741fa86d322SPavel Emelyanov } 742fa86d322SPavel Emelyanov 743be01d655SYOSHIFUJI Hideaki static struct pneigh_entry *__pneigh_lookup_1(struct pneigh_entry *n, 744be01d655SYOSHIFUJI Hideaki struct net *net, 745be01d655SYOSHIFUJI Hideaki const void *pkey, 74601ccdf12SAlexey Dobriyan unsigned int key_len, 747be01d655SYOSHIFUJI Hideaki struct net_device *dev) 748be01d655SYOSHIFUJI Hideaki { 749be01d655SYOSHIFUJI Hideaki while (n) { 750be01d655SYOSHIFUJI Hideaki if (!memcmp(n->key, pkey, key_len) && 751be01d655SYOSHIFUJI Hideaki net_eq(pneigh_net(n), net) && 752be01d655SYOSHIFUJI Hideaki (n->dev == dev || !n->dev)) 753fa86d322SPavel Emelyanov return n; 754be01d655SYOSHIFUJI Hideaki n = n->next; 755be01d655SYOSHIFUJI Hideaki } 756be01d655SYOSHIFUJI Hideaki return NULL; 757be01d655SYOSHIFUJI Hideaki } 758be01d655SYOSHIFUJI Hideaki 759be01d655SYOSHIFUJI Hideaki struct pneigh_entry *__pneigh_lookup(struct neigh_table *tbl, 760be01d655SYOSHIFUJI Hideaki struct net *net, const void *pkey, struct net_device *dev) 761be01d655SYOSHIFUJI Hideaki { 76201ccdf12SAlexey Dobriyan unsigned int key_len = tbl->key_len; 763be01d655SYOSHIFUJI Hideaki u32 hash_val = pneigh_hash(pkey, key_len); 764be01d655SYOSHIFUJI Hideaki 765be01d655SYOSHIFUJI Hideaki return __pneigh_lookup_1(tbl->phash_buckets[hash_val], 766be01d655SYOSHIFUJI Hideaki net, pkey, key_len, dev); 767fa86d322SPavel Emelyanov } 7680a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL_GPL(__pneigh_lookup); 769fa86d322SPavel Emelyanov 770426b5303SEric W. Biederman struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, 771426b5303SEric W. Biederman struct net *net, const void *pkey, 7721da177e4SLinus Torvalds struct net_device *dev, int creat) 7731da177e4SLinus Torvalds { 7741da177e4SLinus Torvalds struct pneigh_entry *n; 77501ccdf12SAlexey Dobriyan unsigned int key_len = tbl->key_len; 776be01d655SYOSHIFUJI Hideaki u32 hash_val = pneigh_hash(pkey, key_len); 7771da177e4SLinus Torvalds 7781da177e4SLinus Torvalds read_lock_bh(&tbl->lock); 779be01d655SYOSHIFUJI Hideaki n = __pneigh_lookup_1(tbl->phash_buckets[hash_val], 780be01d655SYOSHIFUJI Hideaki net, pkey, key_len, dev); 781be01d655SYOSHIFUJI Hideaki read_unlock_bh(&tbl->lock); 7821da177e4SLinus Torvalds 783be01d655SYOSHIFUJI Hideaki if (n || !creat) 7841da177e4SLinus Torvalds goto out; 7851da177e4SLinus Torvalds 7864ae28944SPavel Emelyanov ASSERT_RTNL(); 7874ae28944SPavel Emelyanov 788e195e9b5SEric Dumazet n = kzalloc(sizeof(*n) + key_len, GFP_KERNEL); 7891da177e4SLinus Torvalds if (!n) 7901da177e4SLinus Torvalds goto out; 7911da177e4SLinus Torvalds 792efd7ef1cSEric W. Biederman write_pnet(&n->net, net); 7931da177e4SLinus Torvalds memcpy(n->key, pkey, key_len); 7941da177e4SLinus Torvalds n->dev = dev; 795d62607c3SJakub Kicinski netdev_hold(dev, &n->dev_tracker, GFP_KERNEL); 7961da177e4SLinus Torvalds 7971da177e4SLinus Torvalds if (tbl->pconstructor && tbl->pconstructor(n)) { 798d62607c3SJakub Kicinski netdev_put(dev, &n->dev_tracker); 7991da177e4SLinus Torvalds kfree(n); 8001da177e4SLinus Torvalds n = NULL; 8011da177e4SLinus Torvalds goto out; 8021da177e4SLinus Torvalds } 8031da177e4SLinus Torvalds 8041da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 8051da177e4SLinus Torvalds n->next = tbl->phash_buckets[hash_val]; 8061da177e4SLinus Torvalds tbl->phash_buckets[hash_val] = n; 8071da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 8081da177e4SLinus Torvalds out: 8091da177e4SLinus Torvalds return n; 8101da177e4SLinus Torvalds } 8110a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(pneigh_lookup); 8121da177e4SLinus Torvalds 8131da177e4SLinus Torvalds 814426b5303SEric W. Biederman int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey, 8151da177e4SLinus Torvalds struct net_device *dev) 8161da177e4SLinus Torvalds { 8171da177e4SLinus Torvalds struct pneigh_entry *n, **np; 81801ccdf12SAlexey Dobriyan unsigned int key_len = tbl->key_len; 819be01d655SYOSHIFUJI Hideaki u32 hash_val = pneigh_hash(pkey, key_len); 8201da177e4SLinus Torvalds 8211da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 8221da177e4SLinus Torvalds for (np = &tbl->phash_buckets[hash_val]; (n = *np) != NULL; 8231da177e4SLinus Torvalds np = &n->next) { 824426b5303SEric W. Biederman if (!memcmp(n->key, pkey, key_len) && n->dev == dev && 825878628fbSYOSHIFUJI Hideaki net_eq(pneigh_net(n), net)) { 8261da177e4SLinus Torvalds *np = n->next; 8271da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 8281da177e4SLinus Torvalds if (tbl->pdestructor) 8291da177e4SLinus Torvalds tbl->pdestructor(n); 830d62607c3SJakub Kicinski netdev_put(n->dev, &n->dev_tracker); 8311da177e4SLinus Torvalds kfree(n); 8321da177e4SLinus Torvalds return 0; 8331da177e4SLinus Torvalds } 8341da177e4SLinus Torvalds } 8351da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 8361da177e4SLinus Torvalds return -ENOENT; 8371da177e4SLinus Torvalds } 8381da177e4SLinus Torvalds 83953b76cdfSWolfgang Bumiller static int pneigh_ifdown_and_unlock(struct neigh_table *tbl, 84053b76cdfSWolfgang Bumiller struct net_device *dev) 8411da177e4SLinus Torvalds { 84253b76cdfSWolfgang Bumiller struct pneigh_entry *n, **np, *freelist = NULL; 8431da177e4SLinus Torvalds u32 h; 8441da177e4SLinus Torvalds 8451da177e4SLinus Torvalds for (h = 0; h <= PNEIGH_HASHMASK; h++) { 8461da177e4SLinus Torvalds np = &tbl->phash_buckets[h]; 8471da177e4SLinus Torvalds while ((n = *np) != NULL) { 8481da177e4SLinus Torvalds if (!dev || n->dev == dev) { 8491da177e4SLinus Torvalds *np = n->next; 85053b76cdfSWolfgang Bumiller n->next = freelist; 85153b76cdfSWolfgang Bumiller freelist = n; 85253b76cdfSWolfgang Bumiller continue; 85353b76cdfSWolfgang Bumiller } 85453b76cdfSWolfgang Bumiller np = &n->next; 85553b76cdfSWolfgang Bumiller } 85653b76cdfSWolfgang Bumiller } 85753b76cdfSWolfgang Bumiller write_unlock_bh(&tbl->lock); 85853b76cdfSWolfgang Bumiller while ((n = freelist)) { 85953b76cdfSWolfgang Bumiller freelist = n->next; 86053b76cdfSWolfgang Bumiller n->next = NULL; 8611da177e4SLinus Torvalds if (tbl->pdestructor) 8621da177e4SLinus Torvalds tbl->pdestructor(n); 863d62607c3SJakub Kicinski netdev_put(n->dev, &n->dev_tracker); 8641da177e4SLinus Torvalds kfree(n); 8651da177e4SLinus Torvalds } 8661da177e4SLinus Torvalds return -ENOENT; 8671da177e4SLinus Torvalds } 8681da177e4SLinus Torvalds 86906f0511dSDenis V. Lunev static void neigh_parms_destroy(struct neigh_parms *parms); 87006f0511dSDenis V. Lunev 87106f0511dSDenis V. Lunev static inline void neigh_parms_put(struct neigh_parms *parms) 87206f0511dSDenis V. Lunev { 8736343944bSReshetova, Elena if (refcount_dec_and_test(&parms->refcnt)) 87406f0511dSDenis V. Lunev neigh_parms_destroy(parms); 87506f0511dSDenis V. Lunev } 8761da177e4SLinus Torvalds 8771da177e4SLinus Torvalds /* 8781da177e4SLinus Torvalds * neighbour must already be out of the table; 8791da177e4SLinus Torvalds * 8801da177e4SLinus Torvalds */ 8811da177e4SLinus Torvalds void neigh_destroy(struct neighbour *neigh) 8821da177e4SLinus Torvalds { 883da6a8fa0SDavid Miller struct net_device *dev = neigh->dev; 884da6a8fa0SDavid Miller 8851da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(neigh->tbl, destroys); 8861da177e4SLinus Torvalds 8871da177e4SLinus Torvalds if (!neigh->dead) { 888e005d193SJoe Perches pr_warn("Destroying alive neighbour %p\n", neigh); 8891da177e4SLinus Torvalds dump_stack(); 8901da177e4SLinus Torvalds return; 8911da177e4SLinus Torvalds } 8921da177e4SLinus Torvalds 8931da177e4SLinus Torvalds if (neigh_del_timer(neigh)) 894e005d193SJoe Perches pr_warn("Impossible event\n"); 8951da177e4SLinus Torvalds 896c9ab4d85SEric Dumazet write_lock_bh(&neigh->lock); 897c9ab4d85SEric Dumazet __skb_queue_purge(&neigh->arp_queue); 898c9ab4d85SEric Dumazet write_unlock_bh(&neigh->lock); 8998b5c171bSEric Dumazet neigh->arp_queue_len_bytes = 0; 9001da177e4SLinus Torvalds 901447f2191SDavid S. Miller if (dev->netdev_ops->ndo_neigh_destroy) 902503eebc2SJiri Pirko dev->netdev_ops->ndo_neigh_destroy(dev, neigh); 903447f2191SDavid S. Miller 904d62607c3SJakub Kicinski netdev_put(dev, &neigh->dev_tracker); 9051da177e4SLinus Torvalds neigh_parms_put(neigh->parms); 9061da177e4SLinus Torvalds 907d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is destroyed\n", neigh); 9081da177e4SLinus Torvalds 9091da177e4SLinus Torvalds atomic_dec(&neigh->tbl->entries); 9105b8b0060SDavid Miller kfree_rcu(neigh, rcu); 9111da177e4SLinus Torvalds } 9120a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_destroy); 9131da177e4SLinus Torvalds 9141da177e4SLinus Torvalds /* Neighbour state is suspicious; 9151da177e4SLinus Torvalds disable fast path. 9161da177e4SLinus Torvalds 9171da177e4SLinus Torvalds Called with write_locked neigh. 9181da177e4SLinus Torvalds */ 9191da177e4SLinus Torvalds static void neigh_suspect(struct neighbour *neigh) 9201da177e4SLinus Torvalds { 921d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is suspected\n", neigh); 9221da177e4SLinus Torvalds 9231da177e4SLinus Torvalds neigh->output = neigh->ops->output; 9241da177e4SLinus Torvalds } 9251da177e4SLinus Torvalds 9261da177e4SLinus Torvalds /* Neighbour state is OK; 9271da177e4SLinus Torvalds enable fast path. 9281da177e4SLinus Torvalds 9291da177e4SLinus Torvalds Called with write_locked neigh. 9301da177e4SLinus Torvalds */ 9311da177e4SLinus Torvalds static void neigh_connect(struct neighbour *neigh) 9321da177e4SLinus Torvalds { 933d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is connected\n", neigh); 9341da177e4SLinus Torvalds 9351da177e4SLinus Torvalds neigh->output = neigh->ops->connected_output; 9361da177e4SLinus Torvalds } 9371da177e4SLinus Torvalds 938e4c4e448SEric Dumazet static void neigh_periodic_work(struct work_struct *work) 9391da177e4SLinus Torvalds { 940e4c4e448SEric Dumazet struct neigh_table *tbl = container_of(work, struct neigh_table, gc_work.work); 941767e97e1SEric Dumazet struct neighbour *n; 942767e97e1SEric Dumazet struct neighbour __rcu **np; 943e4c4e448SEric Dumazet unsigned int i; 944d6bf7817SEric Dumazet struct neigh_hash_table *nht; 9451da177e4SLinus Torvalds 9461da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, periodic_gc_runs); 9471da177e4SLinus Torvalds 948e4c4e448SEric Dumazet write_lock_bh(&tbl->lock); 949d6bf7817SEric Dumazet nht = rcu_dereference_protected(tbl->nht, 950d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 9511da177e4SLinus Torvalds 9521da177e4SLinus Torvalds /* 9531da177e4SLinus Torvalds * periodically recompute ReachableTime from random function 9541da177e4SLinus Torvalds */ 9551da177e4SLinus Torvalds 956e4c4e448SEric Dumazet if (time_after(jiffies, tbl->last_rand + 300 * HZ)) { 9571da177e4SLinus Torvalds struct neigh_parms *p; 958e4c4e448SEric Dumazet tbl->last_rand = jiffies; 95975fbfd33SNicolas Dichtel list_for_each_entry(p, &tbl->parms_list, list) 9601da177e4SLinus Torvalds p->reachable_time = 9611f9248e5SJiri Pirko neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); 9621da177e4SLinus Torvalds } 9631da177e4SLinus Torvalds 964feff9ab2SDuan Jiong if (atomic_read(&tbl->entries) < tbl->gc_thresh1) 965feff9ab2SDuan Jiong goto out; 966feff9ab2SDuan Jiong 967cd089336SDavid S. Miller for (i = 0 ; i < (1 << nht->hash_shift); i++) { 968d6bf7817SEric Dumazet np = &nht->hash_buckets[i]; 9691da177e4SLinus Torvalds 970767e97e1SEric Dumazet while ((n = rcu_dereference_protected(*np, 971767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))) != NULL) { 9721da177e4SLinus Torvalds unsigned int state; 9731da177e4SLinus Torvalds 9741da177e4SLinus Torvalds write_lock(&n->lock); 9751da177e4SLinus Torvalds 9761da177e4SLinus Torvalds state = n->nud_state; 9779ce33e46SRoopa Prabhu if ((state & (NUD_PERMANENT | NUD_IN_TIMER)) || 9789ce33e46SRoopa Prabhu (n->flags & NTF_EXT_LEARNED)) { 9791da177e4SLinus Torvalds write_unlock(&n->lock); 9801da177e4SLinus Torvalds goto next_elt; 9811da177e4SLinus Torvalds } 9821da177e4SLinus Torvalds 983c1d2ecdfSJulian Anastasov if (time_before(n->used, n->confirmed) && 984c1d2ecdfSJulian Anastasov time_is_before_eq_jiffies(n->confirmed)) 9851da177e4SLinus Torvalds n->used = n->confirmed; 9861da177e4SLinus Torvalds 9879f237430SReshetova, Elena if (refcount_read(&n->refcnt) == 1 && 9881da177e4SLinus Torvalds (state == NUD_FAILED || 989c1d2ecdfSJulian Anastasov !time_in_range_open(jiffies, n->used, 990c1d2ecdfSJulian Anastasov n->used + NEIGH_VAR(n->parms, GC_STALETIME)))) { 9911da177e4SLinus Torvalds *np = n->next; 99258956317SDavid Ahern neigh_mark_dead(n); 9931da177e4SLinus Torvalds write_unlock(&n->lock); 9944f494554SThomas Graf neigh_cleanup_and_release(n); 9951da177e4SLinus Torvalds continue; 9961da177e4SLinus Torvalds } 9971da177e4SLinus Torvalds write_unlock(&n->lock); 9981da177e4SLinus Torvalds 9991da177e4SLinus Torvalds next_elt: 10001da177e4SLinus Torvalds np = &n->next; 10011da177e4SLinus Torvalds } 1002e4c4e448SEric Dumazet /* 1003e4c4e448SEric Dumazet * It's fine to release lock here, even if hash table 1004e4c4e448SEric Dumazet * grows while we are preempted. 1005e4c4e448SEric Dumazet */ 1006e4c4e448SEric Dumazet write_unlock_bh(&tbl->lock); 1007e4c4e448SEric Dumazet cond_resched(); 1008e4c4e448SEric Dumazet write_lock_bh(&tbl->lock); 100984338a6cSMichel Machado nht = rcu_dereference_protected(tbl->nht, 101084338a6cSMichel Machado lockdep_is_held(&tbl->lock)); 1011e4c4e448SEric Dumazet } 10122724680bSYOSHIFUJI Hideaki / 吉藤英明 out: 10131f9248e5SJiri Pirko /* Cycle through all hash buckets every BASE_REACHABLE_TIME/2 ticks. 10141f9248e5SJiri Pirko * ARP entry timeouts range from 1/2 BASE_REACHABLE_TIME to 3/2 10151f9248e5SJiri Pirko * BASE_REACHABLE_TIME. 10161da177e4SLinus Torvalds */ 1017f618002bSviresh kumar queue_delayed_work(system_power_efficient_wq, &tbl->gc_work, 10181f9248e5SJiri Pirko NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME) >> 1); 1019e4c4e448SEric Dumazet write_unlock_bh(&tbl->lock); 10201da177e4SLinus Torvalds } 10211da177e4SLinus Torvalds 10221da177e4SLinus Torvalds static __inline__ int neigh_max_probes(struct neighbour *n) 10231da177e4SLinus Torvalds { 10241da177e4SLinus Torvalds struct neigh_parms *p = n->parms; 10258da86466SYOSHIFUJI Hideaki/吉藤英明 return NEIGH_VAR(p, UCAST_PROBES) + NEIGH_VAR(p, APP_PROBES) + 10268da86466SYOSHIFUJI Hideaki/吉藤英明 (n->nud_state & NUD_PROBE ? NEIGH_VAR(p, MCAST_REPROBES) : 10278da86466SYOSHIFUJI Hideaki/吉藤英明 NEIGH_VAR(p, MCAST_PROBES)); 10281da177e4SLinus Torvalds } 10291da177e4SLinus Torvalds 10305ef12d98STimo Teras static void neigh_invalidate(struct neighbour *neigh) 10310a141509SEric Dumazet __releases(neigh->lock) 10320a141509SEric Dumazet __acquires(neigh->lock) 10335ef12d98STimo Teras { 10345ef12d98STimo Teras struct sk_buff *skb; 10355ef12d98STimo Teras 10365ef12d98STimo Teras NEIGH_CACHE_STAT_INC(neigh->tbl, res_failed); 1037d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is failed\n", neigh); 10385ef12d98STimo Teras neigh->updated = jiffies; 10395ef12d98STimo Teras 10405ef12d98STimo Teras /* It is very thin place. report_unreachable is very complicated 10415ef12d98STimo Teras routine. Particularly, it can hit the same neighbour entry! 10425ef12d98STimo Teras 10435ef12d98STimo Teras So that, we try to be accurate and avoid dead loop. --ANK 10445ef12d98STimo Teras */ 10455ef12d98STimo Teras while (neigh->nud_state == NUD_FAILED && 10465ef12d98STimo Teras (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) { 10475ef12d98STimo Teras write_unlock(&neigh->lock); 10485ef12d98STimo Teras neigh->ops->error_report(neigh, skb); 10495ef12d98STimo Teras write_lock(&neigh->lock); 10505ef12d98STimo Teras } 1051c9ab4d85SEric Dumazet __skb_queue_purge(&neigh->arp_queue); 10528b5c171bSEric Dumazet neigh->arp_queue_len_bytes = 0; 10535ef12d98STimo Teras } 10545ef12d98STimo Teras 1055cd28ca0aSEric Dumazet static void neigh_probe(struct neighbour *neigh) 1056cd28ca0aSEric Dumazet __releases(neigh->lock) 1057cd28ca0aSEric Dumazet { 10584ed377e3SHannes Frederic Sowa struct sk_buff *skb = skb_peek_tail(&neigh->arp_queue); 1059cd28ca0aSEric Dumazet /* keep skb alive even if arp_queue overflows */ 1060cd28ca0aSEric Dumazet if (skb) 106119125c1aSMartin Zhang skb = skb_clone(skb, GFP_ATOMIC); 1062cd28ca0aSEric Dumazet write_unlock(&neigh->lock); 106348481c8fSEric Dumazet if (neigh->ops->solicit) 1064cd28ca0aSEric Dumazet neigh->ops->solicit(neigh, skb); 1065cd28ca0aSEric Dumazet atomic_inc(&neigh->probes); 106687fff3caSYang Wei consume_skb(skb); 1067cd28ca0aSEric Dumazet } 1068cd28ca0aSEric Dumazet 10691da177e4SLinus Torvalds /* Called when a timer expires for a neighbour entry. */ 10701da177e4SLinus Torvalds 1071e99e88a9SKees Cook static void neigh_timer_handler(struct timer_list *t) 10721da177e4SLinus Torvalds { 10731da177e4SLinus Torvalds unsigned long now, next; 1074e99e88a9SKees Cook struct neighbour *neigh = from_timer(neigh, t, timer); 107595c96174SEric Dumazet unsigned int state; 10761da177e4SLinus Torvalds int notify = 0; 10771da177e4SLinus Torvalds 10781da177e4SLinus Torvalds write_lock(&neigh->lock); 10791da177e4SLinus Torvalds 10801da177e4SLinus Torvalds state = neigh->nud_state; 10811da177e4SLinus Torvalds now = jiffies; 10821da177e4SLinus Torvalds next = now + HZ; 10831da177e4SLinus Torvalds 1084045f7b3bSDavid S. Miller if (!(state & NUD_IN_TIMER)) 10851da177e4SLinus Torvalds goto out; 10861da177e4SLinus Torvalds 10871da177e4SLinus Torvalds if (state & NUD_REACHABLE) { 10881da177e4SLinus Torvalds if (time_before_eq(now, 10891da177e4SLinus Torvalds neigh->confirmed + neigh->parms->reachable_time)) { 1090d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is still alive\n", neigh); 10911da177e4SLinus Torvalds next = neigh->confirmed + neigh->parms->reachable_time; 10921da177e4SLinus Torvalds } else if (time_before_eq(now, 10931f9248e5SJiri Pirko neigh->used + 10941f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) { 1095d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is delayed\n", neigh); 1096*b071af52SEric Dumazet WRITE_ONCE(neigh->nud_state, NUD_DELAY); 1097955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 10981da177e4SLinus Torvalds neigh_suspect(neigh); 10991f9248e5SJiri Pirko next = now + NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME); 11001da177e4SLinus Torvalds } else { 1101d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is suspected\n", neigh); 1102*b071af52SEric Dumazet WRITE_ONCE(neigh->nud_state, NUD_STALE); 1103955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 11041da177e4SLinus Torvalds neigh_suspect(neigh); 11058d71740cSTom Tucker notify = 1; 11061da177e4SLinus Torvalds } 11071da177e4SLinus Torvalds } else if (state & NUD_DELAY) { 11081da177e4SLinus Torvalds if (time_before_eq(now, 11091f9248e5SJiri Pirko neigh->confirmed + 11101f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) { 1111d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is now reachable\n", neigh); 1112*b071af52SEric Dumazet WRITE_ONCE(neigh->nud_state, NUD_REACHABLE); 1113955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 11141da177e4SLinus Torvalds neigh_connect(neigh); 11158d71740cSTom Tucker notify = 1; 11161da177e4SLinus Torvalds next = neigh->confirmed + neigh->parms->reachable_time; 11171da177e4SLinus Torvalds } else { 1118d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is probed\n", neigh); 1119*b071af52SEric Dumazet WRITE_ONCE(neigh->nud_state, NUD_PROBE); 1120955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 11211da177e4SLinus Torvalds atomic_set(&neigh->probes, 0); 1122765c9c63SErik Kline notify = 1; 112319e16d22SHangbin Liu next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), 112419e16d22SHangbin Liu HZ/100); 11251da177e4SLinus Torvalds } 11261da177e4SLinus Torvalds } else { 11271da177e4SLinus Torvalds /* NUD_PROBE|NUD_INCOMPLETE */ 112819e16d22SHangbin Liu next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), HZ/100); 11291da177e4SLinus Torvalds } 11301da177e4SLinus Torvalds 11311da177e4SLinus Torvalds if ((neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) && 11321da177e4SLinus Torvalds atomic_read(&neigh->probes) >= neigh_max_probes(neigh)) { 1133*b071af52SEric Dumazet WRITE_ONCE(neigh->nud_state, NUD_FAILED); 11341da177e4SLinus Torvalds notify = 1; 11355ef12d98STimo Teras neigh_invalidate(neigh); 11365e2c21dcSDuan Jiong goto out; 11371da177e4SLinus Torvalds } 11381da177e4SLinus Torvalds 11391da177e4SLinus Torvalds if (neigh->nud_state & NUD_IN_TIMER) { 114096d10d5bSHangbin Liu if (time_before(next, jiffies + HZ/100)) 114196d10d5bSHangbin Liu next = jiffies + HZ/100; 11426fb9974fSHerbert Xu if (!mod_timer(&neigh->timer, next)) 11436fb9974fSHerbert Xu neigh_hold(neigh); 11441da177e4SLinus Torvalds } 11451da177e4SLinus Torvalds if (neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) { 1146cd28ca0aSEric Dumazet neigh_probe(neigh); 11479ff56607SDavid S. Miller } else { 11481da177e4SLinus Torvalds out: 11491da177e4SLinus Torvalds write_unlock(&neigh->lock); 11509ff56607SDavid S. Miller } 11511da177e4SLinus Torvalds 1152d961db35SThomas Graf if (notify) 11537b8f7a40SRoopa Prabhu neigh_update_notify(neigh, 0); 1154d961db35SThomas Graf 115556dd18a4SRoopa Prabhu trace_neigh_timer_handler(neigh, 0); 115656dd18a4SRoopa Prabhu 11571da177e4SLinus Torvalds neigh_release(neigh); 11581da177e4SLinus Torvalds } 11591da177e4SLinus Torvalds 11604a81f6daSDaniel Borkmann int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb, 11614a81f6daSDaniel Borkmann const bool immediate_ok) 11621da177e4SLinus Torvalds { 11631da177e4SLinus Torvalds int rc; 1164cd28ca0aSEric Dumazet bool immediate_probe = false; 11651da177e4SLinus Torvalds 11661da177e4SLinus Torvalds write_lock_bh(&neigh->lock); 11671da177e4SLinus Torvalds 11681da177e4SLinus Torvalds rc = 0; 11691da177e4SLinus Torvalds if (neigh->nud_state & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE)) 11701da177e4SLinus Torvalds goto out_unlock_bh; 11712c51a97fSJulian Anastasov if (neigh->dead) 11722c51a97fSJulian Anastasov goto out_dead; 11731da177e4SLinus Torvalds 11741da177e4SLinus Torvalds if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) { 11751f9248e5SJiri Pirko if (NEIGH_VAR(neigh->parms, MCAST_PROBES) + 11761f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, APP_PROBES)) { 1177cd28ca0aSEric Dumazet unsigned long next, now = jiffies; 1178cd28ca0aSEric Dumazet 11791f9248e5SJiri Pirko atomic_set(&neigh->probes, 11801f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, UCAST_PROBES)); 1181071c3798SLorenzo Bianconi neigh_del_timer(neigh); 1182*b071af52SEric Dumazet WRITE_ONCE(neigh->nud_state, NUD_INCOMPLETE); 1183cd28ca0aSEric Dumazet neigh->updated = now; 11844a81f6daSDaniel Borkmann if (!immediate_ok) { 11854a81f6daSDaniel Borkmann next = now + 1; 11864a81f6daSDaniel Borkmann } else { 1187cd28ca0aSEric Dumazet immediate_probe = true; 11884a81f6daSDaniel Borkmann next = now + max(NEIGH_VAR(neigh->parms, 11894a81f6daSDaniel Borkmann RETRANS_TIME), 11904a81f6daSDaniel Borkmann HZ / 100); 11914a81f6daSDaniel Borkmann } 11924a81f6daSDaniel Borkmann neigh_add_timer(neigh, next); 11931da177e4SLinus Torvalds } else { 1194*b071af52SEric Dumazet WRITE_ONCE(neigh->nud_state, NUD_FAILED); 1195955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 11961da177e4SLinus Torvalds write_unlock_bh(&neigh->lock); 11971da177e4SLinus Torvalds 1198a5736eddSMenglong Dong kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_FAILED); 11991da177e4SLinus Torvalds return 1; 12001da177e4SLinus Torvalds } 12011da177e4SLinus Torvalds } else if (neigh->nud_state & NUD_STALE) { 1202d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is delayed\n", neigh); 1203071c3798SLorenzo Bianconi neigh_del_timer(neigh); 1204*b071af52SEric Dumazet WRITE_ONCE(neigh->nud_state, NUD_DELAY); 1205955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 12061f9248e5SJiri Pirko neigh_add_timer(neigh, jiffies + 12071f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME)); 12081da177e4SLinus Torvalds } 12091da177e4SLinus Torvalds 12101da177e4SLinus Torvalds if (neigh->nud_state == NUD_INCOMPLETE) { 12111da177e4SLinus Torvalds if (skb) { 12128b5c171bSEric Dumazet while (neigh->arp_queue_len_bytes + skb->truesize > 12131f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, QUEUE_LEN_BYTES)) { 12141da177e4SLinus Torvalds struct sk_buff *buff; 12158b5c171bSEric Dumazet 1216f72051b0SDavid S. Miller buff = __skb_dequeue(&neigh->arp_queue); 12178b5c171bSEric Dumazet if (!buff) 12188b5c171bSEric Dumazet break; 12198b5c171bSEric Dumazet neigh->arp_queue_len_bytes -= buff->truesize; 1220a5736eddSMenglong Dong kfree_skb_reason(buff, SKB_DROP_REASON_NEIGH_QUEUEFULL); 12219a6d276eSNeil Horman NEIGH_CACHE_STAT_INC(neigh->tbl, unres_discards); 12221da177e4SLinus Torvalds } 1223a4731138SEric Dumazet skb_dst_force(skb); 12241da177e4SLinus Torvalds __skb_queue_tail(&neigh->arp_queue, skb); 12258b5c171bSEric Dumazet neigh->arp_queue_len_bytes += skb->truesize; 12261da177e4SLinus Torvalds } 12271da177e4SLinus Torvalds rc = 1; 12281da177e4SLinus Torvalds } 12291da177e4SLinus Torvalds out_unlock_bh: 1230cd28ca0aSEric Dumazet if (immediate_probe) 1231cd28ca0aSEric Dumazet neigh_probe(neigh); 1232cd28ca0aSEric Dumazet else 1233cd28ca0aSEric Dumazet write_unlock(&neigh->lock); 1234cd28ca0aSEric Dumazet local_bh_enable(); 123556dd18a4SRoopa Prabhu trace_neigh_event_send_done(neigh, rc); 12361da177e4SLinus Torvalds return rc; 12372c51a97fSJulian Anastasov 12382c51a97fSJulian Anastasov out_dead: 12392c51a97fSJulian Anastasov if (neigh->nud_state & NUD_STALE) 12402c51a97fSJulian Anastasov goto out_unlock_bh; 12412c51a97fSJulian Anastasov write_unlock_bh(&neigh->lock); 1242a5736eddSMenglong Dong kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_DEAD); 124356dd18a4SRoopa Prabhu trace_neigh_event_send_dead(neigh, 1); 12442c51a97fSJulian Anastasov return 1; 12451da177e4SLinus Torvalds } 12460a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(__neigh_event_send); 12471da177e4SLinus Torvalds 1248f6b72b62SDavid S. Miller static void neigh_update_hhs(struct neighbour *neigh) 12491da177e4SLinus Torvalds { 12501da177e4SLinus Torvalds struct hh_cache *hh; 12513b04dddeSStephen Hemminger void (*update)(struct hh_cache*, const struct net_device*, const unsigned char *) 125291a72a70SDoug Kehn = NULL; 125391a72a70SDoug Kehn 125491a72a70SDoug Kehn if (neigh->dev->header_ops) 125591a72a70SDoug Kehn update = neigh->dev->header_ops->cache_update; 12561da177e4SLinus Torvalds 12571da177e4SLinus Torvalds if (update) { 1258f6b72b62SDavid S. Miller hh = &neigh->hh; 1259c305c6aeSEric Dumazet if (READ_ONCE(hh->hh_len)) { 12603644f0ceSStephen Hemminger write_seqlock_bh(&hh->hh_lock); 12611da177e4SLinus Torvalds update(hh, neigh->dev, neigh->ha); 12623644f0ceSStephen Hemminger write_sequnlock_bh(&hh->hh_lock); 12631da177e4SLinus Torvalds } 12641da177e4SLinus Torvalds } 12651da177e4SLinus Torvalds } 12661da177e4SLinus Torvalds 12671da177e4SLinus Torvalds /* Generic update routine. 12681da177e4SLinus Torvalds -- lladdr is new lladdr or NULL, if it is not supplied. 12691da177e4SLinus Torvalds -- new is new state. 12701da177e4SLinus Torvalds -- flags 12711da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE allows to override existing lladdr, 12721da177e4SLinus Torvalds if it is different. 12731da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE will suspect existing "connected" 12741da177e4SLinus Torvalds lladdr instead of overriding it 12751da177e4SLinus Torvalds if it is different. 12761da177e4SLinus Torvalds NEIGH_UPDATE_F_ADMIN means that the change is administrative. 12773dc20f47SDaniel Borkmann NEIGH_UPDATE_F_USE means that the entry is user triggered. 12787482e384SDaniel Borkmann NEIGH_UPDATE_F_MANAGED means that the entry will be auto-refreshed. 12791da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE_ISROUTER allows to override existing 12801da177e4SLinus Torvalds NTF_ROUTER flag. 12811da177e4SLinus Torvalds NEIGH_UPDATE_F_ISROUTER indicates if the neighbour is known as 12821da177e4SLinus Torvalds a router. 12831da177e4SLinus Torvalds 12841da177e4SLinus Torvalds Caller MUST hold reference count on the entry. 12851da177e4SLinus Torvalds */ 12867a35a50dSDavid Ahern static int __neigh_update(struct neighbour *neigh, const u8 *lladdr, 12877a35a50dSDavid Ahern u8 new, u32 flags, u32 nlmsg_pid, 12887a35a50dSDavid Ahern struct netlink_ext_ack *extack) 12891da177e4SLinus Torvalds { 12907482e384SDaniel Borkmann bool gc_update = false, managed_update = false; 12911da177e4SLinus Torvalds int update_isrouter = 0; 12927482e384SDaniel Borkmann struct net_device *dev; 12937482e384SDaniel Borkmann int err, notify = 0; 12947482e384SDaniel Borkmann u8 old; 12951da177e4SLinus Torvalds 129656dd18a4SRoopa Prabhu trace_neigh_update(neigh, lladdr, new, flags, nlmsg_pid); 129756dd18a4SRoopa Prabhu 12981da177e4SLinus Torvalds write_lock_bh(&neigh->lock); 12991da177e4SLinus Torvalds 13001da177e4SLinus Torvalds dev = neigh->dev; 13011da177e4SLinus Torvalds old = neigh->nud_state; 13021da177e4SLinus Torvalds err = -EPERM; 13031da177e4SLinus Torvalds 1304eb4e8facSChinmay Agarwal if (neigh->dead) { 1305eb4e8facSChinmay Agarwal NL_SET_ERR_MSG(extack, "Neighbor entry is now dead"); 1306eb4e8facSChinmay Agarwal new = old; 1307eb4e8facSChinmay Agarwal goto out; 1308eb4e8facSChinmay Agarwal } 13091da177e4SLinus Torvalds if (!(flags & NEIGH_UPDATE_F_ADMIN) && 13101da177e4SLinus Torvalds (old & (NUD_NOARP | NUD_PERMANENT))) 13111da177e4SLinus Torvalds goto out; 13121da177e4SLinus Torvalds 13137482e384SDaniel Borkmann neigh_update_flags(neigh, flags, ¬ify, &gc_update, &managed_update); 13147482e384SDaniel Borkmann if (flags & (NEIGH_UPDATE_F_USE | NEIGH_UPDATE_F_MANAGED)) { 13153dc20f47SDaniel Borkmann new = old & ~NUD_PERMANENT; 1316*b071af52SEric Dumazet WRITE_ONCE(neigh->nud_state, new); 13173dc20f47SDaniel Borkmann err = 0; 13183dc20f47SDaniel Borkmann goto out; 13193dc20f47SDaniel Borkmann } 13209ce33e46SRoopa Prabhu 13211da177e4SLinus Torvalds if (!(new & NUD_VALID)) { 13221da177e4SLinus Torvalds neigh_del_timer(neigh); 13231da177e4SLinus Torvalds if (old & NUD_CONNECTED) 13241da177e4SLinus Torvalds neigh_suspect(neigh); 1325*b071af52SEric Dumazet WRITE_ONCE(neigh->nud_state, new); 13261da177e4SLinus Torvalds err = 0; 13271da177e4SLinus Torvalds notify = old & NUD_VALID; 1328d2fb4fb8SRoopa Prabhu if ((old & (NUD_INCOMPLETE | NUD_PROBE)) && 13295ef12d98STimo Teras (new & NUD_FAILED)) { 13305ef12d98STimo Teras neigh_invalidate(neigh); 13315ef12d98STimo Teras notify = 1; 13325ef12d98STimo Teras } 13331da177e4SLinus Torvalds goto out; 13341da177e4SLinus Torvalds } 13351da177e4SLinus Torvalds 13361da177e4SLinus Torvalds /* Compare new lladdr with cached one */ 13371da177e4SLinus Torvalds if (!dev->addr_len) { 13381da177e4SLinus Torvalds /* First case: device needs no address. */ 13391da177e4SLinus Torvalds lladdr = neigh->ha; 13401da177e4SLinus Torvalds } else if (lladdr) { 13411da177e4SLinus Torvalds /* The second case: if something is already cached 13421da177e4SLinus Torvalds and a new address is proposed: 13431da177e4SLinus Torvalds - compare new & old 13441da177e4SLinus Torvalds - if they are different, check override flag 13451da177e4SLinus Torvalds */ 13461da177e4SLinus Torvalds if ((old & NUD_VALID) && 13471da177e4SLinus Torvalds !memcmp(lladdr, neigh->ha, dev->addr_len)) 13481da177e4SLinus Torvalds lladdr = neigh->ha; 13491da177e4SLinus Torvalds } else { 13501da177e4SLinus Torvalds /* No address is supplied; if we know something, 13511da177e4SLinus Torvalds use it, otherwise discard the request. 13521da177e4SLinus Torvalds */ 13531da177e4SLinus Torvalds err = -EINVAL; 13547a35a50dSDavid Ahern if (!(old & NUD_VALID)) { 13557a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "No link layer address given"); 13561da177e4SLinus Torvalds goto out; 13577a35a50dSDavid Ahern } 13581da177e4SLinus Torvalds lladdr = neigh->ha; 13591da177e4SLinus Torvalds } 13601da177e4SLinus Torvalds 1361f0e0d044SVasily Khoruzhick /* Update confirmed timestamp for neighbour entry after we 1362f0e0d044SVasily Khoruzhick * received ARP packet even if it doesn't change IP to MAC binding. 1363f0e0d044SVasily Khoruzhick */ 1364f0e0d044SVasily Khoruzhick if (new & NUD_CONNECTED) 1365f0e0d044SVasily Khoruzhick neigh->confirmed = jiffies; 1366f0e0d044SVasily Khoruzhick 13671da177e4SLinus Torvalds /* If entry was valid and address is not changed, 13681da177e4SLinus Torvalds do not change entry state, if new one is STALE. 13691da177e4SLinus Torvalds */ 13701da177e4SLinus Torvalds err = 0; 13711da177e4SLinus Torvalds update_isrouter = flags & NEIGH_UPDATE_F_OVERRIDE_ISROUTER; 13721da177e4SLinus Torvalds if (old & NUD_VALID) { 13731da177e4SLinus Torvalds if (lladdr != neigh->ha && !(flags & NEIGH_UPDATE_F_OVERRIDE)) { 13741da177e4SLinus Torvalds update_isrouter = 0; 13751da177e4SLinus Torvalds if ((flags & NEIGH_UPDATE_F_WEAK_OVERRIDE) && 13761da177e4SLinus Torvalds (old & NUD_CONNECTED)) { 13771da177e4SLinus Torvalds lladdr = neigh->ha; 13781da177e4SLinus Torvalds new = NUD_STALE; 13791da177e4SLinus Torvalds } else 13801da177e4SLinus Torvalds goto out; 13811da177e4SLinus Torvalds } else { 13820e7bbcc1SJulian Anastasov if (lladdr == neigh->ha && new == NUD_STALE && 13830e7bbcc1SJulian Anastasov !(flags & NEIGH_UPDATE_F_ADMIN)) 13841da177e4SLinus Torvalds new = old; 13851da177e4SLinus Torvalds } 13861da177e4SLinus Torvalds } 13871da177e4SLinus Torvalds 1388f0e0d044SVasily Khoruzhick /* Update timestamp only once we know we will make a change to the 138977d71233SIhar Hrachyshka * neighbour entry. Otherwise we risk to move the locktime window with 139077d71233SIhar Hrachyshka * noop updates and ignore relevant ARP updates. 139177d71233SIhar Hrachyshka */ 1392f0e0d044SVasily Khoruzhick if (new != old || lladdr != neigh->ha) 139377d71233SIhar Hrachyshka neigh->updated = jiffies; 139477d71233SIhar Hrachyshka 13951da177e4SLinus Torvalds if (new != old) { 13961da177e4SLinus Torvalds neigh_del_timer(neigh); 1397765c9c63SErik Kline if (new & NUD_PROBE) 1398765c9c63SErik Kline atomic_set(&neigh->probes, 0); 1399a43d8994SPavel Emelyanov if (new & NUD_IN_TIMER) 1400667347f1SDavid S. Miller neigh_add_timer(neigh, (jiffies + 14011da177e4SLinus Torvalds ((new & NUD_REACHABLE) ? 1402667347f1SDavid S. Miller neigh->parms->reachable_time : 1403667347f1SDavid S. Miller 0))); 1404*b071af52SEric Dumazet WRITE_ONCE(neigh->nud_state, new); 140553385d2dSBob Gilligan notify = 1; 14061da177e4SLinus Torvalds } 14071da177e4SLinus Torvalds 14081da177e4SLinus Torvalds if (lladdr != neigh->ha) { 14090ed8ddf4SEric Dumazet write_seqlock(&neigh->ha_lock); 14101da177e4SLinus Torvalds memcpy(&neigh->ha, lladdr, dev->addr_len); 14110ed8ddf4SEric Dumazet write_sequnlock(&neigh->ha_lock); 14121da177e4SLinus Torvalds neigh_update_hhs(neigh); 14131da177e4SLinus Torvalds if (!(new & NUD_CONNECTED)) 14141da177e4SLinus Torvalds neigh->confirmed = jiffies - 14151f9248e5SJiri Pirko (NEIGH_VAR(neigh->parms, BASE_REACHABLE_TIME) << 1); 14161da177e4SLinus Torvalds notify = 1; 14171da177e4SLinus Torvalds } 14181da177e4SLinus Torvalds if (new == old) 14191da177e4SLinus Torvalds goto out; 14201da177e4SLinus Torvalds if (new & NUD_CONNECTED) 14211da177e4SLinus Torvalds neigh_connect(neigh); 14221da177e4SLinus Torvalds else 14231da177e4SLinus Torvalds neigh_suspect(neigh); 14241da177e4SLinus Torvalds if (!(old & NUD_VALID)) { 14251da177e4SLinus Torvalds struct sk_buff *skb; 14261da177e4SLinus Torvalds 14271da177e4SLinus Torvalds /* Again: avoid dead loop if something went wrong */ 14281da177e4SLinus Torvalds 14291da177e4SLinus Torvalds while (neigh->nud_state & NUD_VALID && 14301da177e4SLinus Torvalds (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) { 143169cce1d1SDavid S. Miller struct dst_entry *dst = skb_dst(skb); 143269cce1d1SDavid S. Miller struct neighbour *n2, *n1 = neigh; 14331da177e4SLinus Torvalds write_unlock_bh(&neigh->lock); 1434e049f288Sroy.qing.li@gmail.com 1435e049f288Sroy.qing.li@gmail.com rcu_read_lock(); 143613a43d94SDavid S. Miller 143713a43d94SDavid S. Miller /* Why not just use 'neigh' as-is? The problem is that 143813a43d94SDavid S. Miller * things such as shaper, eql, and sch_teql can end up 143913a43d94SDavid S. Miller * using alternative, different, neigh objects to output 144013a43d94SDavid S. Miller * the packet in the output path. So what we need to do 144113a43d94SDavid S. Miller * here is re-lookup the top-level neigh in the path so 144213a43d94SDavid S. Miller * we can reinject the packet there. 144313a43d94SDavid S. Miller */ 144413a43d94SDavid S. Miller n2 = NULL; 1445d47ec7a0STong Zhu if (dst && dst->obsolete != DST_OBSOLETE_DEAD) { 144613a43d94SDavid S. Miller n2 = dst_neigh_lookup_skb(dst, skb); 144713a43d94SDavid S. Miller if (n2) 144869cce1d1SDavid S. Miller n1 = n2; 144913a43d94SDavid S. Miller } 14508f40b161SDavid S. Miller n1->output(n1, skb); 145113a43d94SDavid S. Miller if (n2) 145213a43d94SDavid S. Miller neigh_release(n2); 1453e049f288Sroy.qing.li@gmail.com rcu_read_unlock(); 1454e049f288Sroy.qing.li@gmail.com 14551da177e4SLinus Torvalds write_lock_bh(&neigh->lock); 14561da177e4SLinus Torvalds } 1457c9ab4d85SEric Dumazet __skb_queue_purge(&neigh->arp_queue); 14588b5c171bSEric Dumazet neigh->arp_queue_len_bytes = 0; 14591da177e4SLinus Torvalds } 14601da177e4SLinus Torvalds out: 1461fc6e8073SRoopa Prabhu if (update_isrouter) 1462fc6e8073SRoopa Prabhu neigh_update_is_router(neigh, flags, ¬ify); 14631da177e4SLinus Torvalds write_unlock_bh(&neigh->lock); 14647482e384SDaniel Borkmann if (((new ^ old) & NUD_PERMANENT) || gc_update) 14659c29a2f5SDavid Ahern neigh_update_gc_list(neigh); 14667482e384SDaniel Borkmann if (managed_update) 14677482e384SDaniel Borkmann neigh_update_managed_list(neigh); 14688d71740cSTom Tucker if (notify) 14697b8f7a40SRoopa Prabhu neigh_update_notify(neigh, nlmsg_pid); 147056dd18a4SRoopa Prabhu trace_neigh_update_done(neigh, err); 14711da177e4SLinus Torvalds return err; 14721da177e4SLinus Torvalds } 14737a35a50dSDavid Ahern 14747a35a50dSDavid Ahern int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, 14757a35a50dSDavid Ahern u32 flags, u32 nlmsg_pid) 14767a35a50dSDavid Ahern { 14777a35a50dSDavid Ahern return __neigh_update(neigh, lladdr, new, flags, nlmsg_pid, NULL); 14787a35a50dSDavid Ahern } 14790a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_update); 14801da177e4SLinus Torvalds 14817e980569SJiri Benc /* Update the neigh to listen temporarily for probe responses, even if it is 14827e980569SJiri Benc * in a NUD_FAILED state. The caller has to hold neigh->lock for writing. 14837e980569SJiri Benc */ 14847e980569SJiri Benc void __neigh_set_probe_once(struct neighbour *neigh) 14857e980569SJiri Benc { 14862c51a97fSJulian Anastasov if (neigh->dead) 14872c51a97fSJulian Anastasov return; 14887e980569SJiri Benc neigh->updated = jiffies; 14897e980569SJiri Benc if (!(neigh->nud_state & NUD_FAILED)) 14907e980569SJiri Benc return; 1491*b071af52SEric Dumazet WRITE_ONCE(neigh->nud_state, NUD_INCOMPLETE); 14922176d5d4SDuan Jiong atomic_set(&neigh->probes, neigh_max_probes(neigh)); 14937e980569SJiri Benc neigh_add_timer(neigh, 149419e16d22SHangbin Liu jiffies + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), 149519e16d22SHangbin Liu HZ/100)); 14967e980569SJiri Benc } 14977e980569SJiri Benc EXPORT_SYMBOL(__neigh_set_probe_once); 14987e980569SJiri Benc 14991da177e4SLinus Torvalds struct neighbour *neigh_event_ns(struct neigh_table *tbl, 15001da177e4SLinus Torvalds u8 *lladdr, void *saddr, 15011da177e4SLinus Torvalds struct net_device *dev) 15021da177e4SLinus Torvalds { 15031da177e4SLinus Torvalds struct neighbour *neigh = __neigh_lookup(tbl, saddr, dev, 15041da177e4SLinus Torvalds lladdr || !dev->addr_len); 15051da177e4SLinus Torvalds if (neigh) 15061da177e4SLinus Torvalds neigh_update(neigh, lladdr, NUD_STALE, 15077b8f7a40SRoopa Prabhu NEIGH_UPDATE_F_OVERRIDE, 0); 15081da177e4SLinus Torvalds return neigh; 15091da177e4SLinus Torvalds } 15100a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_event_ns); 15111da177e4SLinus Torvalds 151234d101ddSEric Dumazet /* called with read_lock_bh(&n->lock); */ 1513bdf53c58SEric W. Biederman static void neigh_hh_init(struct neighbour *n) 15141da177e4SLinus Torvalds { 1515bdf53c58SEric W. Biederman struct net_device *dev = n->dev; 1516bdf53c58SEric W. Biederman __be16 prot = n->tbl->protocol; 1517f6b72b62SDavid S. Miller struct hh_cache *hh = &n->hh; 15180ed8ddf4SEric Dumazet 15190ed8ddf4SEric Dumazet write_lock_bh(&n->lock); 152034d101ddSEric Dumazet 1521f6b72b62SDavid S. Miller /* Only one thread can come in here and initialize the 1522f6b72b62SDavid S. Miller * hh_cache entry. 1523f6b72b62SDavid S. Miller */ 1524b23b5455SDavid S. Miller if (!hh->hh_len) 1525b23b5455SDavid S. Miller dev->header_ops->cache(n, hh, prot); 1526f6b72b62SDavid S. Miller 15270ed8ddf4SEric Dumazet write_unlock_bh(&n->lock); 15281da177e4SLinus Torvalds } 15291da177e4SLinus Torvalds 15301da177e4SLinus Torvalds /* Slow and careful. */ 15311da177e4SLinus Torvalds 15328f40b161SDavid S. Miller int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb) 15331da177e4SLinus Torvalds { 15341da177e4SLinus Torvalds int rc = 0; 15351da177e4SLinus Torvalds 15361da177e4SLinus Torvalds if (!neigh_event_send(neigh, skb)) { 15371da177e4SLinus Torvalds int err; 15381da177e4SLinus Torvalds struct net_device *dev = neigh->dev; 15390ed8ddf4SEric Dumazet unsigned int seq; 154034d101ddSEric Dumazet 1541c305c6aeSEric Dumazet if (dev->header_ops->cache && !READ_ONCE(neigh->hh.hh_len)) 1542bdf53c58SEric W. Biederman neigh_hh_init(neigh); 154334d101ddSEric Dumazet 15440ed8ddf4SEric Dumazet do { 1545e1f16503Sramesh.nagappa@gmail.com __skb_pull(skb, skb_network_offset(skb)); 15460ed8ddf4SEric Dumazet seq = read_seqbegin(&neigh->ha_lock); 15470c4e8581SStephen Hemminger err = dev_hard_header(skb, dev, ntohs(skb->protocol), 15481da177e4SLinus Torvalds neigh->ha, NULL, skb->len); 15490ed8ddf4SEric Dumazet } while (read_seqretry(&neigh->ha_lock, seq)); 155034d101ddSEric Dumazet 15511da177e4SLinus Torvalds if (err >= 0) 1552542d4d68SDavid S. Miller rc = dev_queue_xmit(skb); 15531da177e4SLinus Torvalds else 15541da177e4SLinus Torvalds goto out_kfree_skb; 15551da177e4SLinus Torvalds } 15561da177e4SLinus Torvalds out: 15571da177e4SLinus Torvalds return rc; 15581da177e4SLinus Torvalds out_kfree_skb: 15591da177e4SLinus Torvalds rc = -EINVAL; 15601da177e4SLinus Torvalds kfree_skb(skb); 15611da177e4SLinus Torvalds goto out; 15621da177e4SLinus Torvalds } 15630a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_resolve_output); 15641da177e4SLinus Torvalds 15651da177e4SLinus Torvalds /* As fast as possible without hh cache */ 15661da177e4SLinus Torvalds 15678f40b161SDavid S. Miller int neigh_connected_output(struct neighbour *neigh, struct sk_buff *skb) 15681da177e4SLinus Torvalds { 15691da177e4SLinus Torvalds struct net_device *dev = neigh->dev; 15700ed8ddf4SEric Dumazet unsigned int seq; 15718f40b161SDavid S. Miller int err; 15721da177e4SLinus Torvalds 15730ed8ddf4SEric Dumazet do { 1574e1f16503Sramesh.nagappa@gmail.com __skb_pull(skb, skb_network_offset(skb)); 15750ed8ddf4SEric Dumazet seq = read_seqbegin(&neigh->ha_lock); 15760c4e8581SStephen Hemminger err = dev_hard_header(skb, dev, ntohs(skb->protocol), 15771da177e4SLinus Torvalds neigh->ha, NULL, skb->len); 15780ed8ddf4SEric Dumazet } while (read_seqretry(&neigh->ha_lock, seq)); 15790ed8ddf4SEric Dumazet 15801da177e4SLinus Torvalds if (err >= 0) 1581542d4d68SDavid S. Miller err = dev_queue_xmit(skb); 15821da177e4SLinus Torvalds else { 15831da177e4SLinus Torvalds err = -EINVAL; 15841da177e4SLinus Torvalds kfree_skb(skb); 15851da177e4SLinus Torvalds } 15861da177e4SLinus Torvalds return err; 15871da177e4SLinus Torvalds } 15880a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_connected_output); 15891da177e4SLinus Torvalds 15908f40b161SDavid S. Miller int neigh_direct_output(struct neighbour *neigh, struct sk_buff *skb) 15918f40b161SDavid S. Miller { 15928f40b161SDavid S. Miller return dev_queue_xmit(skb); 15938f40b161SDavid S. Miller } 15948f40b161SDavid S. Miller EXPORT_SYMBOL(neigh_direct_output); 15958f40b161SDavid S. Miller 15967482e384SDaniel Borkmann static void neigh_managed_work(struct work_struct *work) 15977482e384SDaniel Borkmann { 15987482e384SDaniel Borkmann struct neigh_table *tbl = container_of(work, struct neigh_table, 15997482e384SDaniel Borkmann managed_work.work); 16007482e384SDaniel Borkmann struct neighbour *neigh; 16017482e384SDaniel Borkmann 16027482e384SDaniel Borkmann write_lock_bh(&tbl->lock); 16037482e384SDaniel Borkmann list_for_each_entry(neigh, &tbl->managed_list, managed_list) 16044a81f6daSDaniel Borkmann neigh_event_send_probe(neigh, NULL, false); 16057482e384SDaniel Borkmann queue_delayed_work(system_power_efficient_wq, &tbl->managed_work, 1606211da42eSYuwei Wang NEIGH_VAR(&tbl->parms, INTERVAL_PROBE_TIME_MS)); 16077482e384SDaniel Borkmann write_unlock_bh(&tbl->lock); 16087482e384SDaniel Borkmann } 16097482e384SDaniel Borkmann 1610e99e88a9SKees Cook static void neigh_proxy_process(struct timer_list *t) 16111da177e4SLinus Torvalds { 1612e99e88a9SKees Cook struct neigh_table *tbl = from_timer(tbl, t, proxy_timer); 16131da177e4SLinus Torvalds long sched_next = 0; 16141da177e4SLinus Torvalds unsigned long now = jiffies; 1615f72051b0SDavid S. Miller struct sk_buff *skb, *n; 16161da177e4SLinus Torvalds 16171da177e4SLinus Torvalds spin_lock(&tbl->proxy_queue.lock); 16181da177e4SLinus Torvalds 1619f72051b0SDavid S. Miller skb_queue_walk_safe(&tbl->proxy_queue, skb, n) { 1620f72051b0SDavid S. Miller long tdif = NEIGH_CB(skb)->sched_next - now; 16211da177e4SLinus Torvalds 16221da177e4SLinus Torvalds if (tdif <= 0) { 1623f72051b0SDavid S. Miller struct net_device *dev = skb->dev; 162420e6074eSEric Dumazet 16258207f253SThomas Zeitlhofer neigh_parms_qlen_dec(dev, tbl->family); 1626f72051b0SDavid S. Miller __skb_unlink(skb, &tbl->proxy_queue); 16270ff4eb3dSAlexander Mikhalitsyn 162820e6074eSEric Dumazet if (tbl->proxy_redo && netif_running(dev)) { 162920e6074eSEric Dumazet rcu_read_lock(); 1630f72051b0SDavid S. Miller tbl->proxy_redo(skb); 163120e6074eSEric Dumazet rcu_read_unlock(); 163220e6074eSEric Dumazet } else { 1633f72051b0SDavid S. Miller kfree_skb(skb); 163420e6074eSEric Dumazet } 16351da177e4SLinus Torvalds 16361da177e4SLinus Torvalds dev_put(dev); 16371da177e4SLinus Torvalds } else if (!sched_next || tdif < sched_next) 16381da177e4SLinus Torvalds sched_next = tdif; 16391da177e4SLinus Torvalds } 16401da177e4SLinus Torvalds del_timer(&tbl->proxy_timer); 16411da177e4SLinus Torvalds if (sched_next) 16421da177e4SLinus Torvalds mod_timer(&tbl->proxy_timer, jiffies + sched_next); 16431da177e4SLinus Torvalds spin_unlock(&tbl->proxy_queue.lock); 16441da177e4SLinus Torvalds } 16451da177e4SLinus Torvalds 164662e395f8SBrian Haley static unsigned long neigh_proxy_delay(struct neigh_parms *p) 164762e395f8SBrian Haley { 164862e395f8SBrian Haley /* If proxy_delay is zero, do not call get_random_u32_below() 164962e395f8SBrian Haley * as it is undefined behavior. 165062e395f8SBrian Haley */ 165162e395f8SBrian Haley unsigned long proxy_delay = NEIGH_VAR(p, PROXY_DELAY); 165262e395f8SBrian Haley 165362e395f8SBrian Haley return proxy_delay ? 165462e395f8SBrian Haley jiffies + get_random_u32_below(proxy_delay) : jiffies; 165562e395f8SBrian Haley } 165662e395f8SBrian Haley 16571da177e4SLinus Torvalds void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p, 16581da177e4SLinus Torvalds struct sk_buff *skb) 16591da177e4SLinus Torvalds { 166062e395f8SBrian Haley unsigned long sched_next = neigh_proxy_delay(p); 16611da177e4SLinus Torvalds 16620ff4eb3dSAlexander Mikhalitsyn if (p->qlen > NEIGH_VAR(p, PROXY_QLEN)) { 16631da177e4SLinus Torvalds kfree_skb(skb); 16641da177e4SLinus Torvalds return; 16651da177e4SLinus Torvalds } 1666a61bbcf2SPatrick McHardy 1667a61bbcf2SPatrick McHardy NEIGH_CB(skb)->sched_next = sched_next; 1668a61bbcf2SPatrick McHardy NEIGH_CB(skb)->flags |= LOCALLY_ENQUEUED; 16691da177e4SLinus Torvalds 16701da177e4SLinus Torvalds spin_lock(&tbl->proxy_queue.lock); 16711da177e4SLinus Torvalds if (del_timer(&tbl->proxy_timer)) { 16721da177e4SLinus Torvalds if (time_before(tbl->proxy_timer.expires, sched_next)) 16731da177e4SLinus Torvalds sched_next = tbl->proxy_timer.expires; 16741da177e4SLinus Torvalds } 1675adf30907SEric Dumazet skb_dst_drop(skb); 16761da177e4SLinus Torvalds dev_hold(skb->dev); 16771da177e4SLinus Torvalds __skb_queue_tail(&tbl->proxy_queue, skb); 16780ff4eb3dSAlexander Mikhalitsyn p->qlen++; 16791da177e4SLinus Torvalds mod_timer(&tbl->proxy_timer, sched_next); 16801da177e4SLinus Torvalds spin_unlock(&tbl->proxy_queue.lock); 16811da177e4SLinus Torvalds } 16820a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(pneigh_enqueue); 16831da177e4SLinus Torvalds 168497fd5bc7STobias Klauser static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl, 1685426b5303SEric W. Biederman struct net *net, int ifindex) 1686426b5303SEric W. Biederman { 1687426b5303SEric W. Biederman struct neigh_parms *p; 1688426b5303SEric W. Biederman 168975fbfd33SNicolas Dichtel list_for_each_entry(p, &tbl->parms_list, list) { 1690878628fbSYOSHIFUJI Hideaki if ((p->dev && p->dev->ifindex == ifindex && net_eq(neigh_parms_net(p), net)) || 1691170d6f99SGao feng (!p->dev && !ifindex && net_eq(net, &init_net))) 1692426b5303SEric W. Biederman return p; 1693426b5303SEric W. Biederman } 1694426b5303SEric W. Biederman 1695426b5303SEric W. Biederman return NULL; 1696426b5303SEric W. Biederman } 16971da177e4SLinus Torvalds 16981da177e4SLinus Torvalds struct neigh_parms *neigh_parms_alloc(struct net_device *dev, 16991da177e4SLinus Torvalds struct neigh_table *tbl) 17001da177e4SLinus Torvalds { 1701cf89d6b2SGao feng struct neigh_parms *p; 170200829823SStephen Hemminger struct net *net = dev_net(dev); 170300829823SStephen Hemminger const struct net_device_ops *ops = dev->netdev_ops; 17041da177e4SLinus Torvalds 1705cf89d6b2SGao feng p = kmemdup(&tbl->parms, sizeof(*p), GFP_KERNEL); 17061da177e4SLinus Torvalds if (p) { 17071da177e4SLinus Torvalds p->tbl = tbl; 17086343944bSReshetova, Elena refcount_set(&p->refcnt, 1); 17091da177e4SLinus Torvalds p->reachable_time = 17101f9248e5SJiri Pirko neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); 17110ff4eb3dSAlexander Mikhalitsyn p->qlen = 0; 1712d62607c3SJakub Kicinski netdev_hold(dev, &p->dev_tracker, GFP_KERNEL); 1713c7fb64dbSThomas Graf p->dev = dev; 1714efd7ef1cSEric W. Biederman write_pnet(&p->net, net); 17151da177e4SLinus Torvalds p->sysctl_table = NULL; 171663134803SVeaceslav Falico 171763134803SVeaceslav Falico if (ops->ndo_neigh_setup && ops->ndo_neigh_setup(dev, p)) { 1718d62607c3SJakub Kicinski netdev_put(dev, &p->dev_tracker); 171963134803SVeaceslav Falico kfree(p); 172063134803SVeaceslav Falico return NULL; 172163134803SVeaceslav Falico } 172263134803SVeaceslav Falico 17231da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 172475fbfd33SNicolas Dichtel list_add(&p->list, &tbl->parms.list); 17251da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 17261d4c8c29SJiri Pirko 17271d4c8c29SJiri Pirko neigh_parms_data_state_cleanall(p); 17281da177e4SLinus Torvalds } 17291da177e4SLinus Torvalds return p; 17301da177e4SLinus Torvalds } 17310a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_parms_alloc); 17321da177e4SLinus Torvalds 17331da177e4SLinus Torvalds static void neigh_rcu_free_parms(struct rcu_head *head) 17341da177e4SLinus Torvalds { 17351da177e4SLinus Torvalds struct neigh_parms *parms = 17361da177e4SLinus Torvalds container_of(head, struct neigh_parms, rcu_head); 17371da177e4SLinus Torvalds 17381da177e4SLinus Torvalds neigh_parms_put(parms); 17391da177e4SLinus Torvalds } 17401da177e4SLinus Torvalds 17411da177e4SLinus Torvalds void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms) 17421da177e4SLinus Torvalds { 17431da177e4SLinus Torvalds if (!parms || parms == &tbl->parms) 17441da177e4SLinus Torvalds return; 17451da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 174675fbfd33SNicolas Dichtel list_del(&parms->list); 17471da177e4SLinus Torvalds parms->dead = 1; 17481da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 1749d62607c3SJakub Kicinski netdev_put(parms->dev, &parms->dev_tracker); 17501da177e4SLinus Torvalds call_rcu(&parms->rcu_head, neigh_rcu_free_parms); 17511da177e4SLinus Torvalds } 17520a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_parms_release); 17531da177e4SLinus Torvalds 175406f0511dSDenis V. Lunev static void neigh_parms_destroy(struct neigh_parms *parms) 17551da177e4SLinus Torvalds { 17561da177e4SLinus Torvalds kfree(parms); 17571da177e4SLinus Torvalds } 17581da177e4SLinus Torvalds 1759c2ecba71SPavel Emelianov static struct lock_class_key neigh_table_proxy_queue_class; 1760c2ecba71SPavel Emelianov 1761d7480fd3SWANG Cong static struct neigh_table *neigh_tables[NEIGH_NR_TABLES] __read_mostly; 1762d7480fd3SWANG Cong 1763d7480fd3SWANG Cong void neigh_table_init(int index, struct neigh_table *tbl) 17641da177e4SLinus Torvalds { 17651da177e4SLinus Torvalds unsigned long now = jiffies; 17661da177e4SLinus Torvalds unsigned long phsize; 17671da177e4SLinus Torvalds 176875fbfd33SNicolas Dichtel INIT_LIST_HEAD(&tbl->parms_list); 176958956317SDavid Ahern INIT_LIST_HEAD(&tbl->gc_list); 17707482e384SDaniel Borkmann INIT_LIST_HEAD(&tbl->managed_list); 17717482e384SDaniel Borkmann 177275fbfd33SNicolas Dichtel list_add(&tbl->parms.list, &tbl->parms_list); 1773e42ea986SEric Dumazet write_pnet(&tbl->parms.net, &init_net); 17746343944bSReshetova, Elena refcount_set(&tbl->parms.refcnt, 1); 17751da177e4SLinus Torvalds tbl->parms.reachable_time = 17761f9248e5SJiri Pirko neigh_rand_reach_time(NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME)); 17770ff4eb3dSAlexander Mikhalitsyn tbl->parms.qlen = 0; 17781da177e4SLinus Torvalds 17791da177e4SLinus Torvalds tbl->stats = alloc_percpu(struct neigh_statistics); 17801da177e4SLinus Torvalds if (!tbl->stats) 17811da177e4SLinus Torvalds panic("cannot create neighbour cache statistics"); 17821da177e4SLinus Torvalds 17831da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 178471a5053aSChristoph Hellwig if (!proc_create_seq_data(tbl->id, 0, init_net.proc_net_stat, 178571a5053aSChristoph Hellwig &neigh_stat_seq_ops, tbl)) 17861da177e4SLinus Torvalds panic("cannot create neighbour proc dir entry"); 17871da177e4SLinus Torvalds #endif 17881da177e4SLinus Torvalds 1789cd089336SDavid S. Miller RCU_INIT_POINTER(tbl->nht, neigh_hash_alloc(3)); 17901da177e4SLinus Torvalds 17911da177e4SLinus Torvalds phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *); 179277d04bd9SAndrew Morton tbl->phash_buckets = kzalloc(phsize, GFP_KERNEL); 17931da177e4SLinus Torvalds 1794d6bf7817SEric Dumazet if (!tbl->nht || !tbl->phash_buckets) 17951da177e4SLinus Torvalds panic("cannot allocate neighbour cache hashes"); 17961da177e4SLinus Torvalds 179708433effSYOSHIFUJI Hideaki / 吉藤英明 if (!tbl->entry_size) 179808433effSYOSHIFUJI Hideaki / 吉藤英明 tbl->entry_size = ALIGN(offsetof(struct neighbour, primary_key) + 179908433effSYOSHIFUJI Hideaki / 吉藤英明 tbl->key_len, NEIGH_PRIV_ALIGN); 180008433effSYOSHIFUJI Hideaki / 吉藤英明 else 180108433effSYOSHIFUJI Hideaki / 吉藤英明 WARN_ON(tbl->entry_size % NEIGH_PRIV_ALIGN); 180208433effSYOSHIFUJI Hideaki / 吉藤英明 18031da177e4SLinus Torvalds rwlock_init(&tbl->lock); 18047482e384SDaniel Borkmann 1805203b42f7STejun Heo INIT_DEFERRABLE_WORK(&tbl->gc_work, neigh_periodic_work); 1806f618002bSviresh kumar queue_delayed_work(system_power_efficient_wq, &tbl->gc_work, 1807f618002bSviresh kumar tbl->parms.reachable_time); 18087482e384SDaniel Borkmann INIT_DEFERRABLE_WORK(&tbl->managed_work, neigh_managed_work); 18097482e384SDaniel Borkmann queue_delayed_work(system_power_efficient_wq, &tbl->managed_work, 0); 18107482e384SDaniel Borkmann 1811e99e88a9SKees Cook timer_setup(&tbl->proxy_timer, neigh_proxy_process, 0); 1812c2ecba71SPavel Emelianov skb_queue_head_init_class(&tbl->proxy_queue, 1813c2ecba71SPavel Emelianov &neigh_table_proxy_queue_class); 18141da177e4SLinus Torvalds 18151da177e4SLinus Torvalds tbl->last_flush = now; 18161da177e4SLinus Torvalds tbl->last_rand = now + tbl->parms.reachable_time * 20; 1817bd89efc5SSimon Kelley 1818d7480fd3SWANG Cong neigh_tables[index] = tbl; 18191da177e4SLinus Torvalds } 18200a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_table_init); 18211da177e4SLinus Torvalds 1822d7480fd3SWANG Cong int neigh_table_clear(int index, struct neigh_table *tbl) 18231da177e4SLinus Torvalds { 1824d7480fd3SWANG Cong neigh_tables[index] = NULL; 18251da177e4SLinus Torvalds /* It is not clean... Fix it to unload IPv6 module safely */ 18264177d5b0SDaniel Borkmann cancel_delayed_work_sync(&tbl->managed_work); 1827a5c30b34STejun Heo cancel_delayed_work_sync(&tbl->gc_work); 18281da177e4SLinus Torvalds del_timer_sync(&tbl->proxy_timer); 18298207f253SThomas Zeitlhofer pneigh_queue_purge(&tbl->proxy_queue, NULL, tbl->family); 18301da177e4SLinus Torvalds neigh_ifdown(tbl, NULL); 18311da177e4SLinus Torvalds if (atomic_read(&tbl->entries)) 1832e005d193SJoe Perches pr_crit("neighbour leakage\n"); 18331da177e4SLinus Torvalds 18346193d2beSEric Dumazet call_rcu(&rcu_dereference_protected(tbl->nht, 1)->rcu, 18356193d2beSEric Dumazet neigh_hash_free_rcu); 1836d6bf7817SEric Dumazet tbl->nht = NULL; 18371da177e4SLinus Torvalds 18381da177e4SLinus Torvalds kfree(tbl->phash_buckets); 18391da177e4SLinus Torvalds tbl->phash_buckets = NULL; 18401da177e4SLinus Torvalds 18413f192b5cSAlexey Dobriyan remove_proc_entry(tbl->id, init_net.proc_net_stat); 18423f192b5cSAlexey Dobriyan 18433fcde74bSKirill Korotaev free_percpu(tbl->stats); 18443fcde74bSKirill Korotaev tbl->stats = NULL; 18453fcde74bSKirill Korotaev 18461da177e4SLinus Torvalds return 0; 18471da177e4SLinus Torvalds } 18480a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_table_clear); 18491da177e4SLinus Torvalds 1850d7480fd3SWANG Cong static struct neigh_table *neigh_find_table(int family) 1851d7480fd3SWANG Cong { 1852d7480fd3SWANG Cong struct neigh_table *tbl = NULL; 1853d7480fd3SWANG Cong 1854d7480fd3SWANG Cong switch (family) { 1855d7480fd3SWANG Cong case AF_INET: 1856d7480fd3SWANG Cong tbl = neigh_tables[NEIGH_ARP_TABLE]; 1857d7480fd3SWANG Cong break; 1858d7480fd3SWANG Cong case AF_INET6: 1859d7480fd3SWANG Cong tbl = neigh_tables[NEIGH_ND_TABLE]; 1860d7480fd3SWANG Cong break; 1861d7480fd3SWANG Cong } 1862d7480fd3SWANG Cong 1863d7480fd3SWANG Cong return tbl; 1864d7480fd3SWANG Cong } 1865d7480fd3SWANG Cong 186682cbb5c6SRoopa Prabhu const struct nla_policy nda_policy[NDA_MAX+1] = { 18671274e1ccSRoopa Prabhu [NDA_UNSPEC] = { .strict_start_type = NDA_NH_ID }, 186882cbb5c6SRoopa Prabhu [NDA_DST] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, 186982cbb5c6SRoopa Prabhu [NDA_LLADDR] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, 187082cbb5c6SRoopa Prabhu [NDA_CACHEINFO] = { .len = sizeof(struct nda_cacheinfo) }, 187182cbb5c6SRoopa Prabhu [NDA_PROBES] = { .type = NLA_U32 }, 187282cbb5c6SRoopa Prabhu [NDA_VLAN] = { .type = NLA_U16 }, 187382cbb5c6SRoopa Prabhu [NDA_PORT] = { .type = NLA_U16 }, 187482cbb5c6SRoopa Prabhu [NDA_VNI] = { .type = NLA_U32 }, 187582cbb5c6SRoopa Prabhu [NDA_IFINDEX] = { .type = NLA_U32 }, 187682cbb5c6SRoopa Prabhu [NDA_MASTER] = { .type = NLA_U32 }, 1877a9cd3439SDavid Ahern [NDA_PROTOCOL] = { .type = NLA_U8 }, 18781274e1ccSRoopa Prabhu [NDA_NH_ID] = { .type = NLA_U32 }, 1879c8e80c11SDaniel Borkmann [NDA_FLAGS_EXT] = NLA_POLICY_MASK(NLA_U32, NTF_EXT_MASK), 1880899426b3SNikolay Aleksandrov [NDA_FDB_EXT_ATTRS] = { .type = NLA_NESTED }, 188182cbb5c6SRoopa Prabhu }; 188282cbb5c6SRoopa Prabhu 1883c21ef3e3SDavid Ahern static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, 1884c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 18851da177e4SLinus Torvalds { 18863b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1887a14a49d2SThomas Graf struct ndmsg *ndm; 1888a14a49d2SThomas Graf struct nlattr *dst_attr; 18891da177e4SLinus Torvalds struct neigh_table *tbl; 1890d7480fd3SWANG Cong struct neighbour *neigh; 18911da177e4SLinus Torvalds struct net_device *dev = NULL; 1892a14a49d2SThomas Graf int err = -EINVAL; 18931da177e4SLinus Torvalds 1894110b2499SEric Dumazet ASSERT_RTNL(); 1895a14a49d2SThomas Graf if (nlmsg_len(nlh) < sizeof(*ndm)) 18961da177e4SLinus Torvalds goto out; 18971da177e4SLinus Torvalds 1898a14a49d2SThomas Graf dst_attr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_DST); 18997a35a50dSDavid Ahern if (!dst_attr) { 19007a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Network address not specified"); 1901a14a49d2SThomas Graf goto out; 19027a35a50dSDavid Ahern } 1903a14a49d2SThomas Graf 1904a14a49d2SThomas Graf ndm = nlmsg_data(nlh); 1905a14a49d2SThomas Graf if (ndm->ndm_ifindex) { 1906110b2499SEric Dumazet dev = __dev_get_by_index(net, ndm->ndm_ifindex); 1907a14a49d2SThomas Graf if (dev == NULL) { 1908a14a49d2SThomas Graf err = -ENODEV; 1909a14a49d2SThomas Graf goto out; 1910a14a49d2SThomas Graf } 1911a14a49d2SThomas Graf } 1912a14a49d2SThomas Graf 1913d7480fd3SWANG Cong tbl = neigh_find_table(ndm->ndm_family); 1914d7480fd3SWANG Cong if (tbl == NULL) 1915d7480fd3SWANG Cong return -EAFNOSUPPORT; 19161da177e4SLinus Torvalds 19177a35a50dSDavid Ahern if (nla_len(dst_attr) < (int)tbl->key_len) { 19187a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid network address"); 1919110b2499SEric Dumazet goto out; 19207a35a50dSDavid Ahern } 19211da177e4SLinus Torvalds 19221da177e4SLinus Torvalds if (ndm->ndm_flags & NTF_PROXY) { 1923426b5303SEric W. Biederman err = pneigh_delete(tbl, net, nla_data(dst_attr), dev); 1924110b2499SEric Dumazet goto out; 19251da177e4SLinus Torvalds } 19261da177e4SLinus Torvalds 1927a14a49d2SThomas Graf if (dev == NULL) 1928110b2499SEric Dumazet goto out; 19291da177e4SLinus Torvalds 1930a14a49d2SThomas Graf neigh = neigh_lookup(tbl, nla_data(dst_attr), dev); 1931a14a49d2SThomas Graf if (neigh == NULL) { 1932a14a49d2SThomas Graf err = -ENOENT; 1933110b2499SEric Dumazet goto out; 1934a14a49d2SThomas Graf } 1935a14a49d2SThomas Graf 19367a35a50dSDavid Ahern err = __neigh_update(neigh, NULL, NUD_FAILED, 19377a35a50dSDavid Ahern NEIGH_UPDATE_F_OVERRIDE | NEIGH_UPDATE_F_ADMIN, 19387a35a50dSDavid Ahern NETLINK_CB(skb).portid, extack); 19395071034eSSowmini Varadhan write_lock_bh(&tbl->lock); 1940a14a49d2SThomas Graf neigh_release(neigh); 19415071034eSSowmini Varadhan neigh_remove_one(neigh, tbl); 19425071034eSSowmini Varadhan write_unlock_bh(&tbl->lock); 1943a14a49d2SThomas Graf 19441da177e4SLinus Torvalds out: 19451da177e4SLinus Torvalds return err; 19461da177e4SLinus Torvalds } 19471da177e4SLinus Torvalds 1948c21ef3e3SDavid Ahern static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, 1949c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 19501da177e4SLinus Torvalds { 1951f7aa74e4SRoopa Prabhu int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE | 1952f7aa74e4SRoopa Prabhu NEIGH_UPDATE_F_OVERRIDE_ISROUTER; 19533b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 19545208debdSThomas Graf struct ndmsg *ndm; 19555208debdSThomas Graf struct nlattr *tb[NDA_MAX+1]; 19561da177e4SLinus Torvalds struct neigh_table *tbl; 19571da177e4SLinus Torvalds struct net_device *dev = NULL; 1958d7480fd3SWANG Cong struct neighbour *neigh; 1959d7480fd3SWANG Cong void *dst, *lladdr; 1960df9b0e30SDavid Ahern u8 protocol = 0; 19612c611ad9SRoopa Prabhu u32 ndm_flags; 19625208debdSThomas Graf int err; 19631da177e4SLinus Torvalds 1964110b2499SEric Dumazet ASSERT_RTNL(); 19658cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(*ndm), tb, NDA_MAX, 19668cb08174SJohannes Berg nda_policy, extack); 19675208debdSThomas Graf if (err < 0) 19681da177e4SLinus Torvalds goto out; 19691da177e4SLinus Torvalds 19705208debdSThomas Graf err = -EINVAL; 19717a35a50dSDavid Ahern if (!tb[NDA_DST]) { 19727a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Network address not specified"); 19735208debdSThomas Graf goto out; 19747a35a50dSDavid Ahern } 19755208debdSThomas Graf 19765208debdSThomas Graf ndm = nlmsg_data(nlh); 19772c611ad9SRoopa Prabhu ndm_flags = ndm->ndm_flags; 19782c611ad9SRoopa Prabhu if (tb[NDA_FLAGS_EXT]) { 19792c611ad9SRoopa Prabhu u32 ext = nla_get_u32(tb[NDA_FLAGS_EXT]); 19802c611ad9SRoopa Prabhu 1981507c2f1dSDaniel Borkmann BUILD_BUG_ON(sizeof(neigh->flags) * BITS_PER_BYTE < 1982507c2f1dSDaniel Borkmann (sizeof(ndm->ndm_flags) * BITS_PER_BYTE + 1983507c2f1dSDaniel Borkmann hweight32(NTF_EXT_MASK))); 19842c611ad9SRoopa Prabhu ndm_flags |= (ext << NTF_EXT_SHIFT); 19852c611ad9SRoopa Prabhu } 19865208debdSThomas Graf if (ndm->ndm_ifindex) { 1987110b2499SEric Dumazet dev = __dev_get_by_index(net, ndm->ndm_ifindex); 19885208debdSThomas Graf if (dev == NULL) { 19895208debdSThomas Graf err = -ENODEV; 19905208debdSThomas Graf goto out; 19915208debdSThomas Graf } 19925208debdSThomas Graf 19937a35a50dSDavid Ahern if (tb[NDA_LLADDR] && nla_len(tb[NDA_LLADDR]) < dev->addr_len) { 19947a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid link address"); 1995110b2499SEric Dumazet goto out; 19965208debdSThomas Graf } 19977a35a50dSDavid Ahern } 19985208debdSThomas Graf 1999d7480fd3SWANG Cong tbl = neigh_find_table(ndm->ndm_family); 2000d7480fd3SWANG Cong if (tbl == NULL) 2001d7480fd3SWANG Cong return -EAFNOSUPPORT; 20021da177e4SLinus Torvalds 20037a35a50dSDavid Ahern if (nla_len(tb[NDA_DST]) < (int)tbl->key_len) { 20047a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid network address"); 2005110b2499SEric Dumazet goto out; 20067a35a50dSDavid Ahern } 20077a35a50dSDavid Ahern 20085208debdSThomas Graf dst = nla_data(tb[NDA_DST]); 20095208debdSThomas Graf lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL; 20101da177e4SLinus Torvalds 2011a9cd3439SDavid Ahern if (tb[NDA_PROTOCOL]) 2012df9b0e30SDavid Ahern protocol = nla_get_u8(tb[NDA_PROTOCOL]); 20132c611ad9SRoopa Prabhu if (ndm_flags & NTF_PROXY) { 201462dd9318SVille Nuorvala struct pneigh_entry *pn; 201562dd9318SVille Nuorvala 20167482e384SDaniel Borkmann if (ndm_flags & NTF_MANAGED) { 20177482e384SDaniel Borkmann NL_SET_ERR_MSG(extack, "Invalid NTF_* flag combination"); 20187482e384SDaniel Borkmann goto out; 20197482e384SDaniel Borkmann } 20207482e384SDaniel Borkmann 20215208debdSThomas Graf err = -ENOBUFS; 2022426b5303SEric W. Biederman pn = pneigh_lookup(tbl, net, dst, dev, 1); 202362dd9318SVille Nuorvala if (pn) { 20242c611ad9SRoopa Prabhu pn->flags = ndm_flags; 2025df9b0e30SDavid Ahern if (protocol) 2026df9b0e30SDavid Ahern pn->protocol = protocol; 202762dd9318SVille Nuorvala err = 0; 202862dd9318SVille Nuorvala } 2029110b2499SEric Dumazet goto out; 20301da177e4SLinus Torvalds } 20311da177e4SLinus Torvalds 20327a35a50dSDavid Ahern if (!dev) { 20337a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Device not specified"); 2034110b2499SEric Dumazet goto out; 20357a35a50dSDavid Ahern } 20361da177e4SLinus Torvalds 2037b8fb1ab4SDavid Ahern if (tbl->allow_add && !tbl->allow_add(dev, extack)) { 2038b8fb1ab4SDavid Ahern err = -EINVAL; 2039b8fb1ab4SDavid Ahern goto out; 2040b8fb1ab4SDavid Ahern } 2041b8fb1ab4SDavid Ahern 20425208debdSThomas Graf neigh = neigh_lookup(tbl, dst, dev); 20435208debdSThomas Graf if (neigh == NULL) { 204430fc7efaSDaniel Borkmann bool ndm_permanent = ndm->ndm_state & NUD_PERMANENT; 204530fc7efaSDaniel Borkmann bool exempt_from_gc = ndm_permanent || 204630fc7efaSDaniel Borkmann ndm_flags & NTF_EXT_LEARNED; 2047e997f8a2SDavid Ahern 20485208debdSThomas Graf if (!(nlh->nlmsg_flags & NLM_F_CREATE)) { 20491da177e4SLinus Torvalds err = -ENOENT; 2050110b2499SEric Dumazet goto out; 20515208debdSThomas Graf } 205230fc7efaSDaniel Borkmann if (ndm_permanent && (ndm_flags & NTF_MANAGED)) { 205330fc7efaSDaniel Borkmann NL_SET_ERR_MSG(extack, "Invalid NTF_* flag for permanent entry"); 205430fc7efaSDaniel Borkmann err = -EINVAL; 205530fc7efaSDaniel Borkmann goto out; 205630fc7efaSDaniel Borkmann } 20575208debdSThomas Graf 2058e4400bbfSDaniel Borkmann neigh = ___neigh_create(tbl, dst, dev, 20597482e384SDaniel Borkmann ndm_flags & 20607482e384SDaniel Borkmann (NTF_EXT_LEARNED | NTF_MANAGED), 2061e4400bbfSDaniel Borkmann exempt_from_gc, true); 20625208debdSThomas Graf if (IS_ERR(neigh)) { 20635208debdSThomas Graf err = PTR_ERR(neigh); 2064110b2499SEric Dumazet goto out; 20651da177e4SLinus Torvalds } 20665208debdSThomas Graf } else { 20675208debdSThomas Graf if (nlh->nlmsg_flags & NLM_F_EXCL) { 20685208debdSThomas Graf err = -EEXIST; 20695208debdSThomas Graf neigh_release(neigh); 2070110b2499SEric Dumazet goto out; 20711da177e4SLinus Torvalds } 20721da177e4SLinus Torvalds 20735208debdSThomas Graf if (!(nlh->nlmsg_flags & NLM_F_REPLACE)) 2074f7aa74e4SRoopa Prabhu flags &= ~(NEIGH_UPDATE_F_OVERRIDE | 2075f7aa74e4SRoopa Prabhu NEIGH_UPDATE_F_OVERRIDE_ISROUTER); 20765208debdSThomas Graf } 20771da177e4SLinus Torvalds 207838212bb3SRoman Mashak if (protocol) 207938212bb3SRoman Mashak neigh->protocol = protocol; 20802c611ad9SRoopa Prabhu if (ndm_flags & NTF_EXT_LEARNED) 20819ce33e46SRoopa Prabhu flags |= NEIGH_UPDATE_F_EXT_LEARNED; 20822c611ad9SRoopa Prabhu if (ndm_flags & NTF_ROUTER) 2083f7aa74e4SRoopa Prabhu flags |= NEIGH_UPDATE_F_ISROUTER; 20847482e384SDaniel Borkmann if (ndm_flags & NTF_MANAGED) 20857482e384SDaniel Borkmann flags |= NEIGH_UPDATE_F_MANAGED; 20862c611ad9SRoopa Prabhu if (ndm_flags & NTF_USE) 20873dc20f47SDaniel Borkmann flags |= NEIGH_UPDATE_F_USE; 2088f7aa74e4SRoopa Prabhu 20897a35a50dSDavid Ahern err = __neigh_update(neigh, lladdr, ndm->ndm_state, flags, 20907a35a50dSDavid Ahern NETLINK_CB(skb).portid, extack); 20917482e384SDaniel Borkmann if (!err && ndm_flags & (NTF_USE | NTF_MANAGED)) { 20923dc20f47SDaniel Borkmann neigh_event_send(neigh, NULL); 20933dc20f47SDaniel Borkmann err = 0; 20943dc20f47SDaniel Borkmann } 20955208debdSThomas Graf neigh_release(neigh); 20961da177e4SLinus Torvalds out: 20971da177e4SLinus Torvalds return err; 20981da177e4SLinus Torvalds } 20991da177e4SLinus Torvalds 2100c7fb64dbSThomas Graf static int neightbl_fill_parms(struct sk_buff *skb, struct neigh_parms *parms) 2101c7fb64dbSThomas Graf { 2102ca860fb3SThomas Graf struct nlattr *nest; 2103e386c6ebSThomas Graf 2104ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, NDTA_PARMS); 2105ca860fb3SThomas Graf if (nest == NULL) 2106ca860fb3SThomas Graf return -ENOBUFS; 2107c7fb64dbSThomas Graf 21089a6308d7SDavid S. Miller if ((parms->dev && 21099a6308d7SDavid S. Miller nla_put_u32(skb, NDTPA_IFINDEX, parms->dev->ifindex)) || 21106343944bSReshetova, Elena nla_put_u32(skb, NDTPA_REFCNT, refcount_read(&parms->refcnt)) || 21111f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_QUEUE_LENBYTES, 21121f9248e5SJiri Pirko NEIGH_VAR(parms, QUEUE_LEN_BYTES)) || 21138b5c171bSEric Dumazet /* approximative value for deprecated QUEUE_LEN (in packets) */ 21149a6308d7SDavid S. Miller nla_put_u32(skb, NDTPA_QUEUE_LEN, 21151f9248e5SJiri Pirko NEIGH_VAR(parms, QUEUE_LEN_BYTES) / SKB_TRUESIZE(ETH_FRAME_LEN)) || 21161f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_PROXY_QLEN, NEIGH_VAR(parms, PROXY_QLEN)) || 21171f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_APP_PROBES, NEIGH_VAR(parms, APP_PROBES)) || 21181f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_UCAST_PROBES, 21191f9248e5SJiri Pirko NEIGH_VAR(parms, UCAST_PROBES)) || 21201f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_MCAST_PROBES, 21211f9248e5SJiri Pirko NEIGH_VAR(parms, MCAST_PROBES)) || 21228da86466SYOSHIFUJI Hideaki/吉藤英明 nla_put_u32(skb, NDTPA_MCAST_REPROBES, 21238da86466SYOSHIFUJI Hideaki/吉藤英明 NEIGH_VAR(parms, MCAST_REPROBES)) || 21242175d87cSNicolas Dichtel nla_put_msecs(skb, NDTPA_REACHABLE_TIME, parms->reachable_time, 21252175d87cSNicolas Dichtel NDTPA_PAD) || 21269a6308d7SDavid S. Miller nla_put_msecs(skb, NDTPA_BASE_REACHABLE_TIME, 21272175d87cSNicolas Dichtel NEIGH_VAR(parms, BASE_REACHABLE_TIME), NDTPA_PAD) || 21281f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_GC_STALETIME, 21292175d87cSNicolas Dichtel NEIGH_VAR(parms, GC_STALETIME), NDTPA_PAD) || 21309a6308d7SDavid S. Miller nla_put_msecs(skb, NDTPA_DELAY_PROBE_TIME, 21312175d87cSNicolas Dichtel NEIGH_VAR(parms, DELAY_PROBE_TIME), NDTPA_PAD) || 21321f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_RETRANS_TIME, 21332175d87cSNicolas Dichtel NEIGH_VAR(parms, RETRANS_TIME), NDTPA_PAD) || 21341f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_ANYCAST_DELAY, 21352175d87cSNicolas Dichtel NEIGH_VAR(parms, ANYCAST_DELAY), NDTPA_PAD) || 21361f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_PROXY_DELAY, 21372175d87cSNicolas Dichtel NEIGH_VAR(parms, PROXY_DELAY), NDTPA_PAD) || 21381f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_LOCKTIME, 2139211da42eSYuwei Wang NEIGH_VAR(parms, LOCKTIME), NDTPA_PAD) || 2140211da42eSYuwei Wang nla_put_msecs(skb, NDTPA_INTERVAL_PROBE_TIME_MS, 2141211da42eSYuwei Wang NEIGH_VAR(parms, INTERVAL_PROBE_TIME_MS), NDTPA_PAD)) 21429a6308d7SDavid S. Miller goto nla_put_failure; 2143ca860fb3SThomas Graf return nla_nest_end(skb, nest); 2144c7fb64dbSThomas Graf 2145ca860fb3SThomas Graf nla_put_failure: 2146bc3ed28cSThomas Graf nla_nest_cancel(skb, nest); 2147bc3ed28cSThomas Graf return -EMSGSIZE; 2148c7fb64dbSThomas Graf } 2149c7fb64dbSThomas Graf 2150ca860fb3SThomas Graf static int neightbl_fill_info(struct sk_buff *skb, struct neigh_table *tbl, 2151ca860fb3SThomas Graf u32 pid, u32 seq, int type, int flags) 2152c7fb64dbSThomas Graf { 2153c7fb64dbSThomas Graf struct nlmsghdr *nlh; 2154c7fb64dbSThomas Graf struct ndtmsg *ndtmsg; 2155c7fb64dbSThomas Graf 2156ca860fb3SThomas Graf nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndtmsg), flags); 2157ca860fb3SThomas Graf if (nlh == NULL) 215826932566SPatrick McHardy return -EMSGSIZE; 2159c7fb64dbSThomas Graf 2160ca860fb3SThomas Graf ndtmsg = nlmsg_data(nlh); 2161c7fb64dbSThomas Graf 2162c7fb64dbSThomas Graf read_lock_bh(&tbl->lock); 2163c7fb64dbSThomas Graf ndtmsg->ndtm_family = tbl->family; 21649ef1d4c7SPatrick McHardy ndtmsg->ndtm_pad1 = 0; 21659ef1d4c7SPatrick McHardy ndtmsg->ndtm_pad2 = 0; 2166c7fb64dbSThomas Graf 21679a6308d7SDavid S. Miller if (nla_put_string(skb, NDTA_NAME, tbl->id) || 21682175d87cSNicolas Dichtel nla_put_msecs(skb, NDTA_GC_INTERVAL, tbl->gc_interval, NDTA_PAD) || 21699a6308d7SDavid S. Miller nla_put_u32(skb, NDTA_THRESH1, tbl->gc_thresh1) || 21709a6308d7SDavid S. Miller nla_put_u32(skb, NDTA_THRESH2, tbl->gc_thresh2) || 21719a6308d7SDavid S. Miller nla_put_u32(skb, NDTA_THRESH3, tbl->gc_thresh3)) 21729a6308d7SDavid S. Miller goto nla_put_failure; 2173c7fb64dbSThomas Graf { 2174c7fb64dbSThomas Graf unsigned long now = jiffies; 21759d027e3aSEric Dumazet long flush_delta = now - tbl->last_flush; 21769d027e3aSEric Dumazet long rand_delta = now - tbl->last_rand; 2177d6bf7817SEric Dumazet struct neigh_hash_table *nht; 2178c7fb64dbSThomas Graf struct ndt_config ndc = { 2179c7fb64dbSThomas Graf .ndtc_key_len = tbl->key_len, 2180c7fb64dbSThomas Graf .ndtc_entry_size = tbl->entry_size, 2181c7fb64dbSThomas Graf .ndtc_entries = atomic_read(&tbl->entries), 2182c7fb64dbSThomas Graf .ndtc_last_flush = jiffies_to_msecs(flush_delta), 2183c7fb64dbSThomas Graf .ndtc_last_rand = jiffies_to_msecs(rand_delta), 2184c7fb64dbSThomas Graf .ndtc_proxy_qlen = tbl->proxy_queue.qlen, 2185c7fb64dbSThomas Graf }; 2186c7fb64dbSThomas Graf 2187d6bf7817SEric Dumazet rcu_read_lock_bh(); 2188d6bf7817SEric Dumazet nht = rcu_dereference_bh(tbl->nht); 21892c2aba6cSDavid S. Miller ndc.ndtc_hash_rnd = nht->hash_rnd[0]; 2190cd089336SDavid S. Miller ndc.ndtc_hash_mask = ((1 << nht->hash_shift) - 1); 2191d6bf7817SEric Dumazet rcu_read_unlock_bh(); 2192d6bf7817SEric Dumazet 21939a6308d7SDavid S. Miller if (nla_put(skb, NDTA_CONFIG, sizeof(ndc), &ndc)) 21949a6308d7SDavid S. Miller goto nla_put_failure; 2195c7fb64dbSThomas Graf } 2196c7fb64dbSThomas Graf 2197c7fb64dbSThomas Graf { 2198c7fb64dbSThomas Graf int cpu; 2199c7fb64dbSThomas Graf struct ndt_stats ndst; 2200c7fb64dbSThomas Graf 2201c7fb64dbSThomas Graf memset(&ndst, 0, sizeof(ndst)); 2202c7fb64dbSThomas Graf 22036f912042SKAMEZAWA Hiroyuki for_each_possible_cpu(cpu) { 2204c7fb64dbSThomas Graf struct neigh_statistics *st; 2205c7fb64dbSThomas Graf 2206c7fb64dbSThomas Graf st = per_cpu_ptr(tbl->stats, cpu); 2207c7fb64dbSThomas Graf ndst.ndts_allocs += st->allocs; 2208c7fb64dbSThomas Graf ndst.ndts_destroys += st->destroys; 2209c7fb64dbSThomas Graf ndst.ndts_hash_grows += st->hash_grows; 2210c7fb64dbSThomas Graf ndst.ndts_res_failed += st->res_failed; 2211c7fb64dbSThomas Graf ndst.ndts_lookups += st->lookups; 2212c7fb64dbSThomas Graf ndst.ndts_hits += st->hits; 2213c7fb64dbSThomas Graf ndst.ndts_rcv_probes_mcast += st->rcv_probes_mcast; 2214c7fb64dbSThomas Graf ndst.ndts_rcv_probes_ucast += st->rcv_probes_ucast; 2215c7fb64dbSThomas Graf ndst.ndts_periodic_gc_runs += st->periodic_gc_runs; 2216c7fb64dbSThomas Graf ndst.ndts_forced_gc_runs += st->forced_gc_runs; 2217fb811395SRick Jones ndst.ndts_table_fulls += st->table_fulls; 2218c7fb64dbSThomas Graf } 2219c7fb64dbSThomas Graf 2220b676338fSNicolas Dichtel if (nla_put_64bit(skb, NDTA_STATS, sizeof(ndst), &ndst, 2221b676338fSNicolas Dichtel NDTA_PAD)) 22229a6308d7SDavid S. Miller goto nla_put_failure; 2223c7fb64dbSThomas Graf } 2224c7fb64dbSThomas Graf 2225c7fb64dbSThomas Graf BUG_ON(tbl->parms.dev); 2226c7fb64dbSThomas Graf if (neightbl_fill_parms(skb, &tbl->parms) < 0) 2227ca860fb3SThomas Graf goto nla_put_failure; 2228c7fb64dbSThomas Graf 2229c7fb64dbSThomas Graf read_unlock_bh(&tbl->lock); 2230053c095aSJohannes Berg nlmsg_end(skb, nlh); 2231053c095aSJohannes Berg return 0; 2232c7fb64dbSThomas Graf 2233ca860fb3SThomas Graf nla_put_failure: 2234c7fb64dbSThomas Graf read_unlock_bh(&tbl->lock); 223526932566SPatrick McHardy nlmsg_cancel(skb, nlh); 223626932566SPatrick McHardy return -EMSGSIZE; 2237c7fb64dbSThomas Graf } 2238c7fb64dbSThomas Graf 2239ca860fb3SThomas Graf static int neightbl_fill_param_info(struct sk_buff *skb, 2240ca860fb3SThomas Graf struct neigh_table *tbl, 2241c7fb64dbSThomas Graf struct neigh_parms *parms, 2242ca860fb3SThomas Graf u32 pid, u32 seq, int type, 2243ca860fb3SThomas Graf unsigned int flags) 2244c7fb64dbSThomas Graf { 2245c7fb64dbSThomas Graf struct ndtmsg *ndtmsg; 2246c7fb64dbSThomas Graf struct nlmsghdr *nlh; 2247c7fb64dbSThomas Graf 2248ca860fb3SThomas Graf nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndtmsg), flags); 2249ca860fb3SThomas Graf if (nlh == NULL) 225026932566SPatrick McHardy return -EMSGSIZE; 2251c7fb64dbSThomas Graf 2252ca860fb3SThomas Graf ndtmsg = nlmsg_data(nlh); 2253c7fb64dbSThomas Graf 2254c7fb64dbSThomas Graf read_lock_bh(&tbl->lock); 2255c7fb64dbSThomas Graf ndtmsg->ndtm_family = tbl->family; 22569ef1d4c7SPatrick McHardy ndtmsg->ndtm_pad1 = 0; 22579ef1d4c7SPatrick McHardy ndtmsg->ndtm_pad2 = 0; 2258c7fb64dbSThomas Graf 2259ca860fb3SThomas Graf if (nla_put_string(skb, NDTA_NAME, tbl->id) < 0 || 2260ca860fb3SThomas Graf neightbl_fill_parms(skb, parms) < 0) 2261ca860fb3SThomas Graf goto errout; 2262c7fb64dbSThomas Graf 2263c7fb64dbSThomas Graf read_unlock_bh(&tbl->lock); 2264053c095aSJohannes Berg nlmsg_end(skb, nlh); 2265053c095aSJohannes Berg return 0; 2266ca860fb3SThomas Graf errout: 2267c7fb64dbSThomas Graf read_unlock_bh(&tbl->lock); 226826932566SPatrick McHardy nlmsg_cancel(skb, nlh); 226926932566SPatrick McHardy return -EMSGSIZE; 2270c7fb64dbSThomas Graf } 2271c7fb64dbSThomas Graf 2272ef7c79edSPatrick McHardy static const struct nla_policy nl_neightbl_policy[NDTA_MAX+1] = { 22736b3f8674SThomas Graf [NDTA_NAME] = { .type = NLA_STRING }, 22746b3f8674SThomas Graf [NDTA_THRESH1] = { .type = NLA_U32 }, 22756b3f8674SThomas Graf [NDTA_THRESH2] = { .type = NLA_U32 }, 22766b3f8674SThomas Graf [NDTA_THRESH3] = { .type = NLA_U32 }, 22776b3f8674SThomas Graf [NDTA_GC_INTERVAL] = { .type = NLA_U64 }, 22786b3f8674SThomas Graf [NDTA_PARMS] = { .type = NLA_NESTED }, 22796b3f8674SThomas Graf }; 22806b3f8674SThomas Graf 2281ef7c79edSPatrick McHardy static const struct nla_policy nl_ntbl_parm_policy[NDTPA_MAX+1] = { 22826b3f8674SThomas Graf [NDTPA_IFINDEX] = { .type = NLA_U32 }, 22836b3f8674SThomas Graf [NDTPA_QUEUE_LEN] = { .type = NLA_U32 }, 22846b3f8674SThomas Graf [NDTPA_PROXY_QLEN] = { .type = NLA_U32 }, 22856b3f8674SThomas Graf [NDTPA_APP_PROBES] = { .type = NLA_U32 }, 22866b3f8674SThomas Graf [NDTPA_UCAST_PROBES] = { .type = NLA_U32 }, 22876b3f8674SThomas Graf [NDTPA_MCAST_PROBES] = { .type = NLA_U32 }, 22888da86466SYOSHIFUJI Hideaki/吉藤英明 [NDTPA_MCAST_REPROBES] = { .type = NLA_U32 }, 22896b3f8674SThomas Graf [NDTPA_BASE_REACHABLE_TIME] = { .type = NLA_U64 }, 22906b3f8674SThomas Graf [NDTPA_GC_STALETIME] = { .type = NLA_U64 }, 22916b3f8674SThomas Graf [NDTPA_DELAY_PROBE_TIME] = { .type = NLA_U64 }, 22926b3f8674SThomas Graf [NDTPA_RETRANS_TIME] = { .type = NLA_U64 }, 22936b3f8674SThomas Graf [NDTPA_ANYCAST_DELAY] = { .type = NLA_U64 }, 22946b3f8674SThomas Graf [NDTPA_PROXY_DELAY] = { .type = NLA_U64 }, 22956b3f8674SThomas Graf [NDTPA_LOCKTIME] = { .type = NLA_U64 }, 2296211da42eSYuwei Wang [NDTPA_INTERVAL_PROBE_TIME_MS] = { .type = NLA_U64, .min = 1 }, 22976b3f8674SThomas Graf }; 22986b3f8674SThomas Graf 2299c21ef3e3SDavid Ahern static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, 2300c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 2301c7fb64dbSThomas Graf { 23023b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 2303c7fb64dbSThomas Graf struct neigh_table *tbl; 23046b3f8674SThomas Graf struct ndtmsg *ndtmsg; 23056b3f8674SThomas Graf struct nlattr *tb[NDTA_MAX+1]; 2306d7480fd3SWANG Cong bool found = false; 2307d7480fd3SWANG Cong int err, tidx; 2308c7fb64dbSThomas Graf 23098cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(*ndtmsg), tb, NDTA_MAX, 2310c21ef3e3SDavid Ahern nl_neightbl_policy, extack); 23116b3f8674SThomas Graf if (err < 0) 23126b3f8674SThomas Graf goto errout; 2313c7fb64dbSThomas Graf 23146b3f8674SThomas Graf if (tb[NDTA_NAME] == NULL) { 23156b3f8674SThomas Graf err = -EINVAL; 23166b3f8674SThomas Graf goto errout; 23176b3f8674SThomas Graf } 23186b3f8674SThomas Graf 23196b3f8674SThomas Graf ndtmsg = nlmsg_data(nlh); 2320d7480fd3SWANG Cong 2321d7480fd3SWANG Cong for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) { 2322d7480fd3SWANG Cong tbl = neigh_tables[tidx]; 2323d7480fd3SWANG Cong if (!tbl) 2324d7480fd3SWANG Cong continue; 2325c7fb64dbSThomas Graf if (ndtmsg->ndtm_family && tbl->family != ndtmsg->ndtm_family) 2326c7fb64dbSThomas Graf continue; 2327d7480fd3SWANG Cong if (nla_strcmp(tb[NDTA_NAME], tbl->id) == 0) { 2328d7480fd3SWANG Cong found = true; 2329c7fb64dbSThomas Graf break; 2330c7fb64dbSThomas Graf } 2331c7fb64dbSThomas Graf } 2332c7fb64dbSThomas Graf 2333d7480fd3SWANG Cong if (!found) 2334d7480fd3SWANG Cong return -ENOENT; 2335d7480fd3SWANG Cong 2336c7fb64dbSThomas Graf /* 2337c7fb64dbSThomas Graf * We acquire tbl->lock to be nice to the periodic timers and 2338c7fb64dbSThomas Graf * make sure they always see a consistent set of values. 2339c7fb64dbSThomas Graf */ 2340c7fb64dbSThomas Graf write_lock_bh(&tbl->lock); 2341c7fb64dbSThomas Graf 23426b3f8674SThomas Graf if (tb[NDTA_PARMS]) { 23436b3f8674SThomas Graf struct nlattr *tbp[NDTPA_MAX+1]; 2344c7fb64dbSThomas Graf struct neigh_parms *p; 23456b3f8674SThomas Graf int i, ifindex = 0; 2346c7fb64dbSThomas Graf 23478cb08174SJohannes Berg err = nla_parse_nested_deprecated(tbp, NDTPA_MAX, 23488cb08174SJohannes Berg tb[NDTA_PARMS], 2349c21ef3e3SDavid Ahern nl_ntbl_parm_policy, extack); 23506b3f8674SThomas Graf if (err < 0) 23516b3f8674SThomas Graf goto errout_tbl_lock; 2352c7fb64dbSThomas Graf 23536b3f8674SThomas Graf if (tbp[NDTPA_IFINDEX]) 23546b3f8674SThomas Graf ifindex = nla_get_u32(tbp[NDTPA_IFINDEX]); 2355c7fb64dbSThomas Graf 235697fd5bc7STobias Klauser p = lookup_neigh_parms(tbl, net, ifindex); 2357c7fb64dbSThomas Graf if (p == NULL) { 2358c7fb64dbSThomas Graf err = -ENOENT; 23596b3f8674SThomas Graf goto errout_tbl_lock; 2360c7fb64dbSThomas Graf } 2361c7fb64dbSThomas Graf 23626b3f8674SThomas Graf for (i = 1; i <= NDTPA_MAX; i++) { 23636b3f8674SThomas Graf if (tbp[i] == NULL) 23646b3f8674SThomas Graf continue; 2365c7fb64dbSThomas Graf 23666b3f8674SThomas Graf switch (i) { 23676b3f8674SThomas Graf case NDTPA_QUEUE_LEN: 23681f9248e5SJiri Pirko NEIGH_VAR_SET(p, QUEUE_LEN_BYTES, 23691f9248e5SJiri Pirko nla_get_u32(tbp[i]) * 23701f9248e5SJiri Pirko SKB_TRUESIZE(ETH_FRAME_LEN)); 23718b5c171bSEric Dumazet break; 23728b5c171bSEric Dumazet case NDTPA_QUEUE_LENBYTES: 23731f9248e5SJiri Pirko NEIGH_VAR_SET(p, QUEUE_LEN_BYTES, 23741f9248e5SJiri Pirko nla_get_u32(tbp[i])); 23756b3f8674SThomas Graf break; 23766b3f8674SThomas Graf case NDTPA_PROXY_QLEN: 23771f9248e5SJiri Pirko NEIGH_VAR_SET(p, PROXY_QLEN, 23781f9248e5SJiri Pirko nla_get_u32(tbp[i])); 23796b3f8674SThomas Graf break; 23806b3f8674SThomas Graf case NDTPA_APP_PROBES: 23811f9248e5SJiri Pirko NEIGH_VAR_SET(p, APP_PROBES, 23821f9248e5SJiri Pirko nla_get_u32(tbp[i])); 23836b3f8674SThomas Graf break; 23846b3f8674SThomas Graf case NDTPA_UCAST_PROBES: 23851f9248e5SJiri Pirko NEIGH_VAR_SET(p, UCAST_PROBES, 23861f9248e5SJiri Pirko nla_get_u32(tbp[i])); 23876b3f8674SThomas Graf break; 23886b3f8674SThomas Graf case NDTPA_MCAST_PROBES: 23891f9248e5SJiri Pirko NEIGH_VAR_SET(p, MCAST_PROBES, 23901f9248e5SJiri Pirko nla_get_u32(tbp[i])); 23916b3f8674SThomas Graf break; 23928da86466SYOSHIFUJI Hideaki/吉藤英明 case NDTPA_MCAST_REPROBES: 23938da86466SYOSHIFUJI Hideaki/吉藤英明 NEIGH_VAR_SET(p, MCAST_REPROBES, 23948da86466SYOSHIFUJI Hideaki/吉藤英明 nla_get_u32(tbp[i])); 23958da86466SYOSHIFUJI Hideaki/吉藤英明 break; 23966b3f8674SThomas Graf case NDTPA_BASE_REACHABLE_TIME: 23971f9248e5SJiri Pirko NEIGH_VAR_SET(p, BASE_REACHABLE_TIME, 23981f9248e5SJiri Pirko nla_get_msecs(tbp[i])); 23994bf6980dSJean-Francois Remy /* update reachable_time as well, otherwise, the change will 24004bf6980dSJean-Francois Remy * only be effective after the next time neigh_periodic_work 24014bf6980dSJean-Francois Remy * decides to recompute it (can be multiple minutes) 24024bf6980dSJean-Francois Remy */ 24034bf6980dSJean-Francois Remy p->reachable_time = 24044bf6980dSJean-Francois Remy neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); 24056b3f8674SThomas Graf break; 24066b3f8674SThomas Graf case NDTPA_GC_STALETIME: 24071f9248e5SJiri Pirko NEIGH_VAR_SET(p, GC_STALETIME, 24081f9248e5SJiri Pirko nla_get_msecs(tbp[i])); 24096b3f8674SThomas Graf break; 24106b3f8674SThomas Graf case NDTPA_DELAY_PROBE_TIME: 24111f9248e5SJiri Pirko NEIGH_VAR_SET(p, DELAY_PROBE_TIME, 24121f9248e5SJiri Pirko nla_get_msecs(tbp[i])); 24132a4501aeSIdo Schimmel call_netevent_notifiers(NETEVENT_DELAY_PROBE_TIME_UPDATE, p); 24146b3f8674SThomas Graf break; 2415211da42eSYuwei Wang case NDTPA_INTERVAL_PROBE_TIME_MS: 2416211da42eSYuwei Wang NEIGH_VAR_SET(p, INTERVAL_PROBE_TIME_MS, 2417211da42eSYuwei Wang nla_get_msecs(tbp[i])); 2418211da42eSYuwei Wang break; 24196b3f8674SThomas Graf case NDTPA_RETRANS_TIME: 24201f9248e5SJiri Pirko NEIGH_VAR_SET(p, RETRANS_TIME, 24211f9248e5SJiri Pirko nla_get_msecs(tbp[i])); 24226b3f8674SThomas Graf break; 24236b3f8674SThomas Graf case NDTPA_ANYCAST_DELAY: 24243977458cSJiri Pirko NEIGH_VAR_SET(p, ANYCAST_DELAY, 24253977458cSJiri Pirko nla_get_msecs(tbp[i])); 24266b3f8674SThomas Graf break; 24276b3f8674SThomas Graf case NDTPA_PROXY_DELAY: 24283977458cSJiri Pirko NEIGH_VAR_SET(p, PROXY_DELAY, 24293977458cSJiri Pirko nla_get_msecs(tbp[i])); 24306b3f8674SThomas Graf break; 24316b3f8674SThomas Graf case NDTPA_LOCKTIME: 24323977458cSJiri Pirko NEIGH_VAR_SET(p, LOCKTIME, 24333977458cSJiri Pirko nla_get_msecs(tbp[i])); 24346b3f8674SThomas Graf break; 2435c7fb64dbSThomas Graf } 24366b3f8674SThomas Graf } 24376b3f8674SThomas Graf } 24386b3f8674SThomas Graf 2439dc25c676SGao feng err = -ENOENT; 2440dc25c676SGao feng if ((tb[NDTA_THRESH1] || tb[NDTA_THRESH2] || 2441dc25c676SGao feng tb[NDTA_THRESH3] || tb[NDTA_GC_INTERVAL]) && 2442dc25c676SGao feng !net_eq(net, &init_net)) 2443dc25c676SGao feng goto errout_tbl_lock; 2444dc25c676SGao feng 24456b3f8674SThomas Graf if (tb[NDTA_THRESH1]) 24466b3f8674SThomas Graf tbl->gc_thresh1 = nla_get_u32(tb[NDTA_THRESH1]); 24476b3f8674SThomas Graf 24486b3f8674SThomas Graf if (tb[NDTA_THRESH2]) 24496b3f8674SThomas Graf tbl->gc_thresh2 = nla_get_u32(tb[NDTA_THRESH2]); 24506b3f8674SThomas Graf 24516b3f8674SThomas Graf if (tb[NDTA_THRESH3]) 24526b3f8674SThomas Graf tbl->gc_thresh3 = nla_get_u32(tb[NDTA_THRESH3]); 24536b3f8674SThomas Graf 24546b3f8674SThomas Graf if (tb[NDTA_GC_INTERVAL]) 24556b3f8674SThomas Graf tbl->gc_interval = nla_get_msecs(tb[NDTA_GC_INTERVAL]); 2456c7fb64dbSThomas Graf 2457c7fb64dbSThomas Graf err = 0; 2458c7fb64dbSThomas Graf 24596b3f8674SThomas Graf errout_tbl_lock: 2460c7fb64dbSThomas Graf write_unlock_bh(&tbl->lock); 24616b3f8674SThomas Graf errout: 2462c7fb64dbSThomas Graf return err; 2463c7fb64dbSThomas Graf } 2464c7fb64dbSThomas Graf 24659632d47fSDavid Ahern static int neightbl_valid_dump_info(const struct nlmsghdr *nlh, 24669632d47fSDavid Ahern struct netlink_ext_ack *extack) 24679632d47fSDavid Ahern { 24689632d47fSDavid Ahern struct ndtmsg *ndtm; 24699632d47fSDavid Ahern 24709632d47fSDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndtm))) { 24719632d47fSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid header for neighbor table dump request"); 24729632d47fSDavid Ahern return -EINVAL; 24739632d47fSDavid Ahern } 24749632d47fSDavid Ahern 24759632d47fSDavid Ahern ndtm = nlmsg_data(nlh); 24769632d47fSDavid Ahern if (ndtm->ndtm_pad1 || ndtm->ndtm_pad2) { 24779632d47fSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor table dump request"); 24789632d47fSDavid Ahern return -EINVAL; 24799632d47fSDavid Ahern } 24809632d47fSDavid Ahern 24819632d47fSDavid Ahern if (nlmsg_attrlen(nlh, sizeof(*ndtm))) { 24829632d47fSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid data after header in neighbor table dump request"); 24839632d47fSDavid Ahern return -EINVAL; 24849632d47fSDavid Ahern } 24859632d47fSDavid Ahern 24869632d47fSDavid Ahern return 0; 24879632d47fSDavid Ahern } 24889632d47fSDavid Ahern 2489c8822a4eSThomas Graf static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb) 2490c7fb64dbSThomas Graf { 24919632d47fSDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 24923b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 2493ca860fb3SThomas Graf int family, tidx, nidx = 0; 2494ca860fb3SThomas Graf int tbl_skip = cb->args[0]; 2495ca860fb3SThomas Graf int neigh_skip = cb->args[1]; 2496c7fb64dbSThomas Graf struct neigh_table *tbl; 2497c7fb64dbSThomas Graf 24989632d47fSDavid Ahern if (cb->strict_check) { 24999632d47fSDavid Ahern int err = neightbl_valid_dump_info(nlh, cb->extack); 25009632d47fSDavid Ahern 25019632d47fSDavid Ahern if (err < 0) 25029632d47fSDavid Ahern return err; 25039632d47fSDavid Ahern } 25049632d47fSDavid Ahern 25059632d47fSDavid Ahern family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family; 2506c7fb64dbSThomas Graf 2507d7480fd3SWANG Cong for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) { 2508c7fb64dbSThomas Graf struct neigh_parms *p; 2509c7fb64dbSThomas Graf 2510d7480fd3SWANG Cong tbl = neigh_tables[tidx]; 2511d7480fd3SWANG Cong if (!tbl) 2512d7480fd3SWANG Cong continue; 2513d7480fd3SWANG Cong 2514ca860fb3SThomas Graf if (tidx < tbl_skip || (family && tbl->family != family)) 2515c7fb64dbSThomas Graf continue; 2516c7fb64dbSThomas Graf 251715e47304SEric W. Biederman if (neightbl_fill_info(skb, tbl, NETLINK_CB(cb->skb).portid, 25189632d47fSDavid Ahern nlh->nlmsg_seq, RTM_NEWNEIGHTBL, 25197b46a644SDavid S. Miller NLM_F_MULTI) < 0) 2520c7fb64dbSThomas Graf break; 2521c7fb64dbSThomas Graf 252275fbfd33SNicolas Dichtel nidx = 0; 252375fbfd33SNicolas Dichtel p = list_next_entry(&tbl->parms, list); 252475fbfd33SNicolas Dichtel list_for_each_entry_from(p, &tbl->parms_list, list) { 2525878628fbSYOSHIFUJI Hideaki if (!net_eq(neigh_parms_net(p), net)) 2526426b5303SEric W. Biederman continue; 2527426b5303SEric W. Biederman 2528efc683fcSGautam Kachroo if (nidx < neigh_skip) 2529efc683fcSGautam Kachroo goto next; 2530c7fb64dbSThomas Graf 2531ca860fb3SThomas Graf if (neightbl_fill_param_info(skb, tbl, p, 253215e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 25339632d47fSDavid Ahern nlh->nlmsg_seq, 2534ca860fb3SThomas Graf RTM_NEWNEIGHTBL, 25357b46a644SDavid S. Miller NLM_F_MULTI) < 0) 2536c7fb64dbSThomas Graf goto out; 2537efc683fcSGautam Kachroo next: 2538efc683fcSGautam Kachroo nidx++; 2539c7fb64dbSThomas Graf } 2540c7fb64dbSThomas Graf 2541ca860fb3SThomas Graf neigh_skip = 0; 2542c7fb64dbSThomas Graf } 2543c7fb64dbSThomas Graf out: 2544ca860fb3SThomas Graf cb->args[0] = tidx; 2545ca860fb3SThomas Graf cb->args[1] = nidx; 2546c7fb64dbSThomas Graf 2547c7fb64dbSThomas Graf return skb->len; 2548c7fb64dbSThomas Graf } 25491da177e4SLinus Torvalds 25508b8aec50SThomas Graf static int neigh_fill_info(struct sk_buff *skb, struct neighbour *neigh, 25518b8aec50SThomas Graf u32 pid, u32 seq, int type, unsigned int flags) 25521da177e4SLinus Torvalds { 25532c611ad9SRoopa Prabhu u32 neigh_flags, neigh_flags_ext; 25541da177e4SLinus Torvalds unsigned long now = jiffies; 25551da177e4SLinus Torvalds struct nda_cacheinfo ci; 25568b8aec50SThomas Graf struct nlmsghdr *nlh; 25578b8aec50SThomas Graf struct ndmsg *ndm; 25581da177e4SLinus Torvalds 25598b8aec50SThomas Graf nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags); 25608b8aec50SThomas Graf if (nlh == NULL) 256126932566SPatrick McHardy return -EMSGSIZE; 25628b8aec50SThomas Graf 25632c611ad9SRoopa Prabhu neigh_flags_ext = neigh->flags >> NTF_EXT_SHIFT; 25642c611ad9SRoopa Prabhu neigh_flags = neigh->flags & NTF_OLD_MASK; 25652c611ad9SRoopa Prabhu 25668b8aec50SThomas Graf ndm = nlmsg_data(nlh); 25678b8aec50SThomas Graf ndm->ndm_family = neigh->ops->family; 25689ef1d4c7SPatrick McHardy ndm->ndm_pad1 = 0; 25699ef1d4c7SPatrick McHardy ndm->ndm_pad2 = 0; 25702c611ad9SRoopa Prabhu ndm->ndm_flags = neigh_flags; 25718b8aec50SThomas Graf ndm->ndm_type = neigh->type; 25728b8aec50SThomas Graf ndm->ndm_ifindex = neigh->dev->ifindex; 25731da177e4SLinus Torvalds 25749a6308d7SDavid S. Miller if (nla_put(skb, NDA_DST, neigh->tbl->key_len, neigh->primary_key)) 25759a6308d7SDavid S. Miller goto nla_put_failure; 25768b8aec50SThomas Graf 25778b8aec50SThomas Graf read_lock_bh(&neigh->lock); 25788b8aec50SThomas Graf ndm->ndm_state = neigh->nud_state; 25790ed8ddf4SEric Dumazet if (neigh->nud_state & NUD_VALID) { 25800ed8ddf4SEric Dumazet char haddr[MAX_ADDR_LEN]; 25810ed8ddf4SEric Dumazet 25820ed8ddf4SEric Dumazet neigh_ha_snapshot(haddr, neigh, neigh->dev); 25830ed8ddf4SEric Dumazet if (nla_put(skb, NDA_LLADDR, neigh->dev->addr_len, haddr) < 0) { 25848b8aec50SThomas Graf read_unlock_bh(&neigh->lock); 25858b8aec50SThomas Graf goto nla_put_failure; 25868b8aec50SThomas Graf } 25870ed8ddf4SEric Dumazet } 25888b8aec50SThomas Graf 2589b9f5f52cSStephen Hemminger ci.ndm_used = jiffies_to_clock_t(now - neigh->used); 2590b9f5f52cSStephen Hemminger ci.ndm_confirmed = jiffies_to_clock_t(now - neigh->confirmed); 2591b9f5f52cSStephen Hemminger ci.ndm_updated = jiffies_to_clock_t(now - neigh->updated); 25929f237430SReshetova, Elena ci.ndm_refcnt = refcount_read(&neigh->refcnt) - 1; 25938b8aec50SThomas Graf read_unlock_bh(&neigh->lock); 25948b8aec50SThomas Graf 25959a6308d7SDavid S. Miller if (nla_put_u32(skb, NDA_PROBES, atomic_read(&neigh->probes)) || 25969a6308d7SDavid S. Miller nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci)) 25979a6308d7SDavid S. Miller goto nla_put_failure; 25988b8aec50SThomas Graf 2599df9b0e30SDavid Ahern if (neigh->protocol && nla_put_u8(skb, NDA_PROTOCOL, neigh->protocol)) 2600df9b0e30SDavid Ahern goto nla_put_failure; 26012c611ad9SRoopa Prabhu if (neigh_flags_ext && nla_put_u32(skb, NDA_FLAGS_EXT, neigh_flags_ext)) 26022c611ad9SRoopa Prabhu goto nla_put_failure; 2603df9b0e30SDavid Ahern 2604053c095aSJohannes Berg nlmsg_end(skb, nlh); 2605053c095aSJohannes Berg return 0; 26068b8aec50SThomas Graf 26078b8aec50SThomas Graf nla_put_failure: 260826932566SPatrick McHardy nlmsg_cancel(skb, nlh); 260926932566SPatrick McHardy return -EMSGSIZE; 26101da177e4SLinus Torvalds } 26111da177e4SLinus Torvalds 261284920c14STony Zelenoff static int pneigh_fill_info(struct sk_buff *skb, struct pneigh_entry *pn, 261384920c14STony Zelenoff u32 pid, u32 seq, int type, unsigned int flags, 261484920c14STony Zelenoff struct neigh_table *tbl) 261584920c14STony Zelenoff { 26162c611ad9SRoopa Prabhu u32 neigh_flags, neigh_flags_ext; 261784920c14STony Zelenoff struct nlmsghdr *nlh; 261884920c14STony Zelenoff struct ndmsg *ndm; 261984920c14STony Zelenoff 262084920c14STony Zelenoff nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags); 262184920c14STony Zelenoff if (nlh == NULL) 262284920c14STony Zelenoff return -EMSGSIZE; 262384920c14STony Zelenoff 26242c611ad9SRoopa Prabhu neigh_flags_ext = pn->flags >> NTF_EXT_SHIFT; 26252c611ad9SRoopa Prabhu neigh_flags = pn->flags & NTF_OLD_MASK; 26262c611ad9SRoopa Prabhu 262784920c14STony Zelenoff ndm = nlmsg_data(nlh); 262884920c14STony Zelenoff ndm->ndm_family = tbl->family; 262984920c14STony Zelenoff ndm->ndm_pad1 = 0; 263084920c14STony Zelenoff ndm->ndm_pad2 = 0; 26312c611ad9SRoopa Prabhu ndm->ndm_flags = neigh_flags | NTF_PROXY; 2632545469f7SJun Zhao ndm->ndm_type = RTN_UNICAST; 26336adc5fd6SKonstantin Khlebnikov ndm->ndm_ifindex = pn->dev ? pn->dev->ifindex : 0; 263484920c14STony Zelenoff ndm->ndm_state = NUD_NONE; 263584920c14STony Zelenoff 26369a6308d7SDavid S. Miller if (nla_put(skb, NDA_DST, tbl->key_len, pn->key)) 26379a6308d7SDavid S. Miller goto nla_put_failure; 263884920c14STony Zelenoff 2639df9b0e30SDavid Ahern if (pn->protocol && nla_put_u8(skb, NDA_PROTOCOL, pn->protocol)) 2640df9b0e30SDavid Ahern goto nla_put_failure; 26412c611ad9SRoopa Prabhu if (neigh_flags_ext && nla_put_u32(skb, NDA_FLAGS_EXT, neigh_flags_ext)) 26422c611ad9SRoopa Prabhu goto nla_put_failure; 2643df9b0e30SDavid Ahern 2644053c095aSJohannes Berg nlmsg_end(skb, nlh); 2645053c095aSJohannes Berg return 0; 264684920c14STony Zelenoff 264784920c14STony Zelenoff nla_put_failure: 264884920c14STony Zelenoff nlmsg_cancel(skb, nlh); 264984920c14STony Zelenoff return -EMSGSIZE; 265084920c14STony Zelenoff } 265184920c14STony Zelenoff 26527b8f7a40SRoopa Prabhu static void neigh_update_notify(struct neighbour *neigh, u32 nlmsg_pid) 2653d961db35SThomas Graf { 2654d961db35SThomas Graf call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh); 26557b8f7a40SRoopa Prabhu __neigh_notify(neigh, RTM_NEWNEIGH, 0, nlmsg_pid); 2656d961db35SThomas Graf } 26571da177e4SLinus Torvalds 265821fdd092SDavid Ahern static bool neigh_master_filtered(struct net_device *dev, int master_idx) 265921fdd092SDavid Ahern { 266021fdd092SDavid Ahern struct net_device *master; 266121fdd092SDavid Ahern 266221fdd092SDavid Ahern if (!master_idx) 266321fdd092SDavid Ahern return false; 266421fdd092SDavid Ahern 2665aab456dfSEric Dumazet master = dev ? netdev_master_upper_dev_get(dev) : NULL; 2666d3432bf1SLahav Schlesinger 2667d3432bf1SLahav Schlesinger /* 0 is already used to denote NDA_MASTER wasn't passed, therefore need another 2668d3432bf1SLahav Schlesinger * invalid value for ifindex to denote "no master". 2669d3432bf1SLahav Schlesinger */ 2670d3432bf1SLahav Schlesinger if (master_idx == -1) 2671d3432bf1SLahav Schlesinger return !!master; 2672d3432bf1SLahav Schlesinger 267321fdd092SDavid Ahern if (!master || master->ifindex != master_idx) 267421fdd092SDavid Ahern return true; 267521fdd092SDavid Ahern 267621fdd092SDavid Ahern return false; 267721fdd092SDavid Ahern } 267821fdd092SDavid Ahern 267916660f0bSDavid Ahern static bool neigh_ifindex_filtered(struct net_device *dev, int filter_idx) 268016660f0bSDavid Ahern { 2681aab456dfSEric Dumazet if (filter_idx && (!dev || dev->ifindex != filter_idx)) 268216660f0bSDavid Ahern return true; 268316660f0bSDavid Ahern 268416660f0bSDavid Ahern return false; 268516660f0bSDavid Ahern } 268616660f0bSDavid Ahern 26876f52f80eSDavid Ahern struct neigh_dump_filter { 26886f52f80eSDavid Ahern int master_idx; 26896f52f80eSDavid Ahern int dev_idx; 26906f52f80eSDavid Ahern }; 26916f52f80eSDavid Ahern 26921da177e4SLinus Torvalds static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, 26936f52f80eSDavid Ahern struct netlink_callback *cb, 26946f52f80eSDavid Ahern struct neigh_dump_filter *filter) 26951da177e4SLinus Torvalds { 26963b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 26971da177e4SLinus Torvalds struct neighbour *n; 26981da177e4SLinus Torvalds int rc, h, s_h = cb->args[1]; 26991da177e4SLinus Torvalds int idx, s_idx = idx = cb->args[2]; 2700d6bf7817SEric Dumazet struct neigh_hash_table *nht; 270121fdd092SDavid Ahern unsigned int flags = NLM_F_MULTI; 270221fdd092SDavid Ahern 27036f52f80eSDavid Ahern if (filter->dev_idx || filter->master_idx) 270421fdd092SDavid Ahern flags |= NLM_F_DUMP_FILTERED; 27051da177e4SLinus Torvalds 2706d6bf7817SEric Dumazet rcu_read_lock_bh(); 2707d6bf7817SEric Dumazet nht = rcu_dereference_bh(tbl->nht); 2708d6bf7817SEric Dumazet 27094bd6683bSEric Dumazet for (h = s_h; h < (1 << nht->hash_shift); h++) { 27101da177e4SLinus Torvalds if (h > s_h) 27111da177e4SLinus Torvalds s_idx = 0; 2712767e97e1SEric Dumazet for (n = rcu_dereference_bh(nht->hash_buckets[h]), idx = 0; 2713767e97e1SEric Dumazet n != NULL; 2714767e97e1SEric Dumazet n = rcu_dereference_bh(n->next)) { 271518502acdSZhang Shengju if (idx < s_idx || !net_eq(dev_net(n->dev), net)) 271618502acdSZhang Shengju goto next; 27176f52f80eSDavid Ahern if (neigh_ifindex_filtered(n->dev, filter->dev_idx) || 27186f52f80eSDavid Ahern neigh_master_filtered(n->dev, filter->master_idx)) 2719efc683fcSGautam Kachroo goto next; 272015e47304SEric W. Biederman if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid, 27211da177e4SLinus Torvalds cb->nlh->nlmsg_seq, 2722b6544c0bSJamal Hadi Salim RTM_NEWNEIGH, 272321fdd092SDavid Ahern flags) < 0) { 27241da177e4SLinus Torvalds rc = -1; 27251da177e4SLinus Torvalds goto out; 27261da177e4SLinus Torvalds } 2727efc683fcSGautam Kachroo next: 2728efc683fcSGautam Kachroo idx++; 27291da177e4SLinus Torvalds } 27301da177e4SLinus Torvalds } 27311da177e4SLinus Torvalds rc = skb->len; 27321da177e4SLinus Torvalds out: 2733d6bf7817SEric Dumazet rcu_read_unlock_bh(); 27341da177e4SLinus Torvalds cb->args[1] = h; 27351da177e4SLinus Torvalds cb->args[2] = idx; 27361da177e4SLinus Torvalds return rc; 27371da177e4SLinus Torvalds } 27381da177e4SLinus Torvalds 273984920c14STony Zelenoff static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, 27406f52f80eSDavid Ahern struct netlink_callback *cb, 27416f52f80eSDavid Ahern struct neigh_dump_filter *filter) 274284920c14STony Zelenoff { 274384920c14STony Zelenoff struct pneigh_entry *n; 274484920c14STony Zelenoff struct net *net = sock_net(skb->sk); 274584920c14STony Zelenoff int rc, h, s_h = cb->args[3]; 274684920c14STony Zelenoff int idx, s_idx = idx = cb->args[4]; 27476f52f80eSDavid Ahern unsigned int flags = NLM_F_MULTI; 27486f52f80eSDavid Ahern 27496f52f80eSDavid Ahern if (filter->dev_idx || filter->master_idx) 27506f52f80eSDavid Ahern flags |= NLM_F_DUMP_FILTERED; 275184920c14STony Zelenoff 275284920c14STony Zelenoff read_lock_bh(&tbl->lock); 275384920c14STony Zelenoff 27544bd6683bSEric Dumazet for (h = s_h; h <= PNEIGH_HASHMASK; h++) { 275584920c14STony Zelenoff if (h > s_h) 275684920c14STony Zelenoff s_idx = 0; 275784920c14STony Zelenoff for (n = tbl->phash_buckets[h], idx = 0; n; n = n->next) { 275818502acdSZhang Shengju if (idx < s_idx || pneigh_net(n) != net) 275984920c14STony Zelenoff goto next; 27606f52f80eSDavid Ahern if (neigh_ifindex_filtered(n->dev, filter->dev_idx) || 27616f52f80eSDavid Ahern neigh_master_filtered(n->dev, filter->master_idx)) 27626f52f80eSDavid Ahern goto next; 276315e47304SEric W. Biederman if (pneigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid, 276484920c14STony Zelenoff cb->nlh->nlmsg_seq, 27656f52f80eSDavid Ahern RTM_NEWNEIGH, flags, tbl) < 0) { 276684920c14STony Zelenoff read_unlock_bh(&tbl->lock); 276784920c14STony Zelenoff rc = -1; 276884920c14STony Zelenoff goto out; 276984920c14STony Zelenoff } 277084920c14STony Zelenoff next: 277184920c14STony Zelenoff idx++; 277284920c14STony Zelenoff } 277384920c14STony Zelenoff } 277484920c14STony Zelenoff 277584920c14STony Zelenoff read_unlock_bh(&tbl->lock); 277684920c14STony Zelenoff rc = skb->len; 277784920c14STony Zelenoff out: 277884920c14STony Zelenoff cb->args[3] = h; 277984920c14STony Zelenoff cb->args[4] = idx; 278084920c14STony Zelenoff return rc; 278184920c14STony Zelenoff 278284920c14STony Zelenoff } 278384920c14STony Zelenoff 278451183d23SDavid Ahern static int neigh_valid_dump_req(const struct nlmsghdr *nlh, 278551183d23SDavid Ahern bool strict_check, 278651183d23SDavid Ahern struct neigh_dump_filter *filter, 278751183d23SDavid Ahern struct netlink_ext_ack *extack) 278851183d23SDavid Ahern { 278951183d23SDavid Ahern struct nlattr *tb[NDA_MAX + 1]; 279051183d23SDavid Ahern int err, i; 279151183d23SDavid Ahern 279251183d23SDavid Ahern if (strict_check) { 279351183d23SDavid Ahern struct ndmsg *ndm; 279451183d23SDavid Ahern 279551183d23SDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) { 279651183d23SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid header for neighbor dump request"); 279751183d23SDavid Ahern return -EINVAL; 279851183d23SDavid Ahern } 279951183d23SDavid Ahern 280051183d23SDavid Ahern ndm = nlmsg_data(nlh); 280151183d23SDavid Ahern if (ndm->ndm_pad1 || ndm->ndm_pad2 || ndm->ndm_ifindex || 2802c0fde870SDavid Ahern ndm->ndm_state || ndm->ndm_type) { 280351183d23SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor dump request"); 280451183d23SDavid Ahern return -EINVAL; 280551183d23SDavid Ahern } 280651183d23SDavid Ahern 2807c0fde870SDavid Ahern if (ndm->ndm_flags & ~NTF_PROXY) { 2808c0fde870SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor dump request"); 2809c0fde870SDavid Ahern return -EINVAL; 2810c0fde870SDavid Ahern } 2811c0fde870SDavid Ahern 28128cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct ndmsg), 28138cb08174SJohannes Berg tb, NDA_MAX, nda_policy, 28148cb08174SJohannes Berg extack); 281551183d23SDavid Ahern } else { 28168cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(struct ndmsg), tb, 28178cb08174SJohannes Berg NDA_MAX, nda_policy, extack); 281851183d23SDavid Ahern } 281951183d23SDavid Ahern if (err < 0) 282051183d23SDavid Ahern return err; 282151183d23SDavid Ahern 282251183d23SDavid Ahern for (i = 0; i <= NDA_MAX; ++i) { 282351183d23SDavid Ahern if (!tb[i]) 282451183d23SDavid Ahern continue; 282551183d23SDavid Ahern 282651183d23SDavid Ahern /* all new attributes should require strict_check */ 282751183d23SDavid Ahern switch (i) { 282851183d23SDavid Ahern case NDA_IFINDEX: 282951183d23SDavid Ahern filter->dev_idx = nla_get_u32(tb[i]); 283051183d23SDavid Ahern break; 283151183d23SDavid Ahern case NDA_MASTER: 283251183d23SDavid Ahern filter->master_idx = nla_get_u32(tb[i]); 283351183d23SDavid Ahern break; 283451183d23SDavid Ahern default: 283551183d23SDavid Ahern if (strict_check) { 283651183d23SDavid Ahern NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor dump request"); 283751183d23SDavid Ahern return -EINVAL; 283851183d23SDavid Ahern } 283951183d23SDavid Ahern } 284051183d23SDavid Ahern } 284151183d23SDavid Ahern 284251183d23SDavid Ahern return 0; 284351183d23SDavid Ahern } 284451183d23SDavid Ahern 2845c8822a4eSThomas Graf static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb) 28461da177e4SLinus Torvalds { 28476f52f80eSDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 28486f52f80eSDavid Ahern struct neigh_dump_filter filter = {}; 28491da177e4SLinus Torvalds struct neigh_table *tbl; 28501da177e4SLinus Torvalds int t, family, s_t; 285184920c14STony Zelenoff int proxy = 0; 28524bd6683bSEric Dumazet int err; 28531da177e4SLinus Torvalds 28546f52f80eSDavid Ahern family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family; 285584920c14STony Zelenoff 285684920c14STony Zelenoff /* check for full ndmsg structure presence, family member is 285784920c14STony Zelenoff * the same for both structures 285884920c14STony Zelenoff */ 28596f52f80eSDavid Ahern if (nlmsg_len(nlh) >= sizeof(struct ndmsg) && 28606f52f80eSDavid Ahern ((struct ndmsg *)nlmsg_data(nlh))->ndm_flags == NTF_PROXY) 286184920c14STony Zelenoff proxy = 1; 286284920c14STony Zelenoff 286351183d23SDavid Ahern err = neigh_valid_dump_req(nlh, cb->strict_check, &filter, cb->extack); 286451183d23SDavid Ahern if (err < 0 && cb->strict_check) 286551183d23SDavid Ahern return err; 286651183d23SDavid Ahern 28671da177e4SLinus Torvalds s_t = cb->args[0]; 28681da177e4SLinus Torvalds 2869d7480fd3SWANG Cong for (t = 0; t < NEIGH_NR_TABLES; t++) { 2870d7480fd3SWANG Cong tbl = neigh_tables[t]; 2871d7480fd3SWANG Cong 2872d7480fd3SWANG Cong if (!tbl) 2873d7480fd3SWANG Cong continue; 28741da177e4SLinus Torvalds if (t < s_t || (family && tbl->family != family)) 28751da177e4SLinus Torvalds continue; 28761da177e4SLinus Torvalds if (t > s_t) 28771da177e4SLinus Torvalds memset(&cb->args[1], 0, sizeof(cb->args) - 28781da177e4SLinus Torvalds sizeof(cb->args[0])); 287984920c14STony Zelenoff if (proxy) 28806f52f80eSDavid Ahern err = pneigh_dump_table(tbl, skb, cb, &filter); 288184920c14STony Zelenoff else 28826f52f80eSDavid Ahern err = neigh_dump_table(tbl, skb, cb, &filter); 28834bd6683bSEric Dumazet if (err < 0) 28844bd6683bSEric Dumazet break; 28851da177e4SLinus Torvalds } 28861da177e4SLinus Torvalds 28871da177e4SLinus Torvalds cb->args[0] = t; 28881da177e4SLinus Torvalds return skb->len; 28891da177e4SLinus Torvalds } 28901da177e4SLinus Torvalds 289182cbb5c6SRoopa Prabhu static int neigh_valid_get_req(const struct nlmsghdr *nlh, 289282cbb5c6SRoopa Prabhu struct neigh_table **tbl, 289382cbb5c6SRoopa Prabhu void **dst, int *dev_idx, u8 *ndm_flags, 289482cbb5c6SRoopa Prabhu struct netlink_ext_ack *extack) 289582cbb5c6SRoopa Prabhu { 289682cbb5c6SRoopa Prabhu struct nlattr *tb[NDA_MAX + 1]; 289782cbb5c6SRoopa Prabhu struct ndmsg *ndm; 289882cbb5c6SRoopa Prabhu int err, i; 289982cbb5c6SRoopa Prabhu 290082cbb5c6SRoopa Prabhu if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) { 290182cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Invalid header for neighbor get request"); 290282cbb5c6SRoopa Prabhu return -EINVAL; 290382cbb5c6SRoopa Prabhu } 290482cbb5c6SRoopa Prabhu 290582cbb5c6SRoopa Prabhu ndm = nlmsg_data(nlh); 290682cbb5c6SRoopa Prabhu if (ndm->ndm_pad1 || ndm->ndm_pad2 || ndm->ndm_state || 290782cbb5c6SRoopa Prabhu ndm->ndm_type) { 290882cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor get request"); 290982cbb5c6SRoopa Prabhu return -EINVAL; 291082cbb5c6SRoopa Prabhu } 291182cbb5c6SRoopa Prabhu 291282cbb5c6SRoopa Prabhu if (ndm->ndm_flags & ~NTF_PROXY) { 291382cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor get request"); 291482cbb5c6SRoopa Prabhu return -EINVAL; 291582cbb5c6SRoopa Prabhu } 291682cbb5c6SRoopa Prabhu 29178cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct ndmsg), tb, 29188cb08174SJohannes Berg NDA_MAX, nda_policy, extack); 291982cbb5c6SRoopa Prabhu if (err < 0) 292082cbb5c6SRoopa Prabhu return err; 292182cbb5c6SRoopa Prabhu 292282cbb5c6SRoopa Prabhu *ndm_flags = ndm->ndm_flags; 292382cbb5c6SRoopa Prabhu *dev_idx = ndm->ndm_ifindex; 292482cbb5c6SRoopa Prabhu *tbl = neigh_find_table(ndm->ndm_family); 292582cbb5c6SRoopa Prabhu if (*tbl == NULL) { 292682cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Unsupported family in header for neighbor get request"); 292782cbb5c6SRoopa Prabhu return -EAFNOSUPPORT; 292882cbb5c6SRoopa Prabhu } 292982cbb5c6SRoopa Prabhu 293082cbb5c6SRoopa Prabhu for (i = 0; i <= NDA_MAX; ++i) { 293182cbb5c6SRoopa Prabhu if (!tb[i]) 293282cbb5c6SRoopa Prabhu continue; 293382cbb5c6SRoopa Prabhu 293482cbb5c6SRoopa Prabhu switch (i) { 293582cbb5c6SRoopa Prabhu case NDA_DST: 293682cbb5c6SRoopa Prabhu if (nla_len(tb[i]) != (int)(*tbl)->key_len) { 293782cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Invalid network address in neighbor get request"); 293882cbb5c6SRoopa Prabhu return -EINVAL; 293982cbb5c6SRoopa Prabhu } 294082cbb5c6SRoopa Prabhu *dst = nla_data(tb[i]); 294182cbb5c6SRoopa Prabhu break; 294282cbb5c6SRoopa Prabhu default: 294382cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor get request"); 294482cbb5c6SRoopa Prabhu return -EINVAL; 294582cbb5c6SRoopa Prabhu } 294682cbb5c6SRoopa Prabhu } 294782cbb5c6SRoopa Prabhu 294882cbb5c6SRoopa Prabhu return 0; 294982cbb5c6SRoopa Prabhu } 295082cbb5c6SRoopa Prabhu 295182cbb5c6SRoopa Prabhu static inline size_t neigh_nlmsg_size(void) 295282cbb5c6SRoopa Prabhu { 295382cbb5c6SRoopa Prabhu return NLMSG_ALIGN(sizeof(struct ndmsg)) 295482cbb5c6SRoopa Prabhu + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */ 295582cbb5c6SRoopa Prabhu + nla_total_size(MAX_ADDR_LEN) /* NDA_LLADDR */ 295682cbb5c6SRoopa Prabhu + nla_total_size(sizeof(struct nda_cacheinfo)) 295782cbb5c6SRoopa Prabhu + nla_total_size(4) /* NDA_PROBES */ 29582c611ad9SRoopa Prabhu + nla_total_size(4) /* NDA_FLAGS_EXT */ 295982cbb5c6SRoopa Prabhu + nla_total_size(1); /* NDA_PROTOCOL */ 296082cbb5c6SRoopa Prabhu } 296182cbb5c6SRoopa Prabhu 296282cbb5c6SRoopa Prabhu static int neigh_get_reply(struct net *net, struct neighbour *neigh, 296382cbb5c6SRoopa Prabhu u32 pid, u32 seq) 296482cbb5c6SRoopa Prabhu { 296582cbb5c6SRoopa Prabhu struct sk_buff *skb; 296682cbb5c6SRoopa Prabhu int err = 0; 296782cbb5c6SRoopa Prabhu 296882cbb5c6SRoopa Prabhu skb = nlmsg_new(neigh_nlmsg_size(), GFP_KERNEL); 296982cbb5c6SRoopa Prabhu if (!skb) 297082cbb5c6SRoopa Prabhu return -ENOBUFS; 297182cbb5c6SRoopa Prabhu 297282cbb5c6SRoopa Prabhu err = neigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0); 297382cbb5c6SRoopa Prabhu if (err) { 297482cbb5c6SRoopa Prabhu kfree_skb(skb); 297582cbb5c6SRoopa Prabhu goto errout; 297682cbb5c6SRoopa Prabhu } 297782cbb5c6SRoopa Prabhu 297882cbb5c6SRoopa Prabhu err = rtnl_unicast(skb, net, pid); 297982cbb5c6SRoopa Prabhu errout: 298082cbb5c6SRoopa Prabhu return err; 298182cbb5c6SRoopa Prabhu } 298282cbb5c6SRoopa Prabhu 298382cbb5c6SRoopa Prabhu static inline size_t pneigh_nlmsg_size(void) 298482cbb5c6SRoopa Prabhu { 298582cbb5c6SRoopa Prabhu return NLMSG_ALIGN(sizeof(struct ndmsg)) 2986463561e6SColin Ian King + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */ 29872c611ad9SRoopa Prabhu + nla_total_size(4) /* NDA_FLAGS_EXT */ 298882cbb5c6SRoopa Prabhu + nla_total_size(1); /* NDA_PROTOCOL */ 298982cbb5c6SRoopa Prabhu } 299082cbb5c6SRoopa Prabhu 299182cbb5c6SRoopa Prabhu static int pneigh_get_reply(struct net *net, struct pneigh_entry *neigh, 299282cbb5c6SRoopa Prabhu u32 pid, u32 seq, struct neigh_table *tbl) 299382cbb5c6SRoopa Prabhu { 299482cbb5c6SRoopa Prabhu struct sk_buff *skb; 299582cbb5c6SRoopa Prabhu int err = 0; 299682cbb5c6SRoopa Prabhu 299782cbb5c6SRoopa Prabhu skb = nlmsg_new(pneigh_nlmsg_size(), GFP_KERNEL); 299882cbb5c6SRoopa Prabhu if (!skb) 299982cbb5c6SRoopa Prabhu return -ENOBUFS; 300082cbb5c6SRoopa Prabhu 300182cbb5c6SRoopa Prabhu err = pneigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0, tbl); 300282cbb5c6SRoopa Prabhu if (err) { 300382cbb5c6SRoopa Prabhu kfree_skb(skb); 300482cbb5c6SRoopa Prabhu goto errout; 300582cbb5c6SRoopa Prabhu } 300682cbb5c6SRoopa Prabhu 300782cbb5c6SRoopa Prabhu err = rtnl_unicast(skb, net, pid); 300882cbb5c6SRoopa Prabhu errout: 300982cbb5c6SRoopa Prabhu return err; 301082cbb5c6SRoopa Prabhu } 301182cbb5c6SRoopa Prabhu 301282cbb5c6SRoopa Prabhu static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh, 301382cbb5c6SRoopa Prabhu struct netlink_ext_ack *extack) 301482cbb5c6SRoopa Prabhu { 301582cbb5c6SRoopa Prabhu struct net *net = sock_net(in_skb->sk); 301682cbb5c6SRoopa Prabhu struct net_device *dev = NULL; 301782cbb5c6SRoopa Prabhu struct neigh_table *tbl = NULL; 301882cbb5c6SRoopa Prabhu struct neighbour *neigh; 301982cbb5c6SRoopa Prabhu void *dst = NULL; 302082cbb5c6SRoopa Prabhu u8 ndm_flags = 0; 302182cbb5c6SRoopa Prabhu int dev_idx = 0; 302282cbb5c6SRoopa Prabhu int err; 302382cbb5c6SRoopa Prabhu 302482cbb5c6SRoopa Prabhu err = neigh_valid_get_req(nlh, &tbl, &dst, &dev_idx, &ndm_flags, 302582cbb5c6SRoopa Prabhu extack); 302682cbb5c6SRoopa Prabhu if (err < 0) 302782cbb5c6SRoopa Prabhu return err; 302882cbb5c6SRoopa Prabhu 302982cbb5c6SRoopa Prabhu if (dev_idx) { 303082cbb5c6SRoopa Prabhu dev = __dev_get_by_index(net, dev_idx); 303182cbb5c6SRoopa Prabhu if (!dev) { 303282cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Unknown device ifindex"); 303382cbb5c6SRoopa Prabhu return -ENODEV; 303482cbb5c6SRoopa Prabhu } 303582cbb5c6SRoopa Prabhu } 303682cbb5c6SRoopa Prabhu 303782cbb5c6SRoopa Prabhu if (!dst) { 303882cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Network address not specified"); 303982cbb5c6SRoopa Prabhu return -EINVAL; 304082cbb5c6SRoopa Prabhu } 304182cbb5c6SRoopa Prabhu 304282cbb5c6SRoopa Prabhu if (ndm_flags & NTF_PROXY) { 304382cbb5c6SRoopa Prabhu struct pneigh_entry *pn; 304482cbb5c6SRoopa Prabhu 304582cbb5c6SRoopa Prabhu pn = pneigh_lookup(tbl, net, dst, dev, 0); 304682cbb5c6SRoopa Prabhu if (!pn) { 304782cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Proxy neighbour entry not found"); 304882cbb5c6SRoopa Prabhu return -ENOENT; 304982cbb5c6SRoopa Prabhu } 305082cbb5c6SRoopa Prabhu return pneigh_get_reply(net, pn, NETLINK_CB(in_skb).portid, 305182cbb5c6SRoopa Prabhu nlh->nlmsg_seq, tbl); 305282cbb5c6SRoopa Prabhu } 305382cbb5c6SRoopa Prabhu 305482cbb5c6SRoopa Prabhu if (!dev) { 305582cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "No device specified"); 305682cbb5c6SRoopa Prabhu return -EINVAL; 305782cbb5c6SRoopa Prabhu } 305882cbb5c6SRoopa Prabhu 305982cbb5c6SRoopa Prabhu neigh = neigh_lookup(tbl, dst, dev); 306082cbb5c6SRoopa Prabhu if (!neigh) { 306182cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Neighbour entry not found"); 306282cbb5c6SRoopa Prabhu return -ENOENT; 306382cbb5c6SRoopa Prabhu } 306482cbb5c6SRoopa Prabhu 306582cbb5c6SRoopa Prabhu err = neigh_get_reply(net, neigh, NETLINK_CB(in_skb).portid, 306682cbb5c6SRoopa Prabhu nlh->nlmsg_seq); 306782cbb5c6SRoopa Prabhu 306882cbb5c6SRoopa Prabhu neigh_release(neigh); 306982cbb5c6SRoopa Prabhu 307082cbb5c6SRoopa Prabhu return err; 307182cbb5c6SRoopa Prabhu } 307282cbb5c6SRoopa Prabhu 30731da177e4SLinus Torvalds void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie) 30741da177e4SLinus Torvalds { 30751da177e4SLinus Torvalds int chain; 3076d6bf7817SEric Dumazet struct neigh_hash_table *nht; 30771da177e4SLinus Torvalds 3078d6bf7817SEric Dumazet rcu_read_lock_bh(); 3079d6bf7817SEric Dumazet nht = rcu_dereference_bh(tbl->nht); 3080d6bf7817SEric Dumazet 3081767e97e1SEric Dumazet read_lock(&tbl->lock); /* avoid resizes */ 3082cd089336SDavid S. Miller for (chain = 0; chain < (1 << nht->hash_shift); chain++) { 30831da177e4SLinus Torvalds struct neighbour *n; 30841da177e4SLinus Torvalds 3085767e97e1SEric Dumazet for (n = rcu_dereference_bh(nht->hash_buckets[chain]); 3086767e97e1SEric Dumazet n != NULL; 3087767e97e1SEric Dumazet n = rcu_dereference_bh(n->next)) 30881da177e4SLinus Torvalds cb(n, cookie); 30891da177e4SLinus Torvalds } 3090d6bf7817SEric Dumazet read_unlock(&tbl->lock); 3091d6bf7817SEric Dumazet rcu_read_unlock_bh(); 30921da177e4SLinus Torvalds } 30931da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_for_each); 30941da177e4SLinus Torvalds 30951da177e4SLinus Torvalds /* The tbl->lock must be held as a writer and BH disabled. */ 30961da177e4SLinus Torvalds void __neigh_for_each_release(struct neigh_table *tbl, 30971da177e4SLinus Torvalds int (*cb)(struct neighbour *)) 30981da177e4SLinus Torvalds { 30991da177e4SLinus Torvalds int chain; 3100d6bf7817SEric Dumazet struct neigh_hash_table *nht; 31011da177e4SLinus Torvalds 3102d6bf7817SEric Dumazet nht = rcu_dereference_protected(tbl->nht, 3103d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 3104cd089336SDavid S. Miller for (chain = 0; chain < (1 << nht->hash_shift); chain++) { 3105767e97e1SEric Dumazet struct neighbour *n; 3106767e97e1SEric Dumazet struct neighbour __rcu **np; 31071da177e4SLinus Torvalds 3108d6bf7817SEric Dumazet np = &nht->hash_buckets[chain]; 3109767e97e1SEric Dumazet while ((n = rcu_dereference_protected(*np, 3110767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))) != NULL) { 31111da177e4SLinus Torvalds int release; 31121da177e4SLinus Torvalds 31131da177e4SLinus Torvalds write_lock(&n->lock); 31141da177e4SLinus Torvalds release = cb(n); 31151da177e4SLinus Torvalds if (release) { 3116767e97e1SEric Dumazet rcu_assign_pointer(*np, 3117767e97e1SEric Dumazet rcu_dereference_protected(n->next, 3118767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))); 311958956317SDavid Ahern neigh_mark_dead(n); 31201da177e4SLinus Torvalds } else 31211da177e4SLinus Torvalds np = &n->next; 31221da177e4SLinus Torvalds write_unlock(&n->lock); 31234f494554SThomas Graf if (release) 31244f494554SThomas Graf neigh_cleanup_and_release(n); 31251da177e4SLinus Torvalds } 31261da177e4SLinus Torvalds } 3127ecbb4169SAlexey Kuznetsov } 31281da177e4SLinus Torvalds EXPORT_SYMBOL(__neigh_for_each_release); 31291da177e4SLinus Torvalds 3130b79bda3dSEric W. Biederman int neigh_xmit(int index, struct net_device *dev, 31314fd3d7d9SEric W. Biederman const void *addr, struct sk_buff *skb) 31324fd3d7d9SEric W. Biederman { 3133b79bda3dSEric W. Biederman int err = -EAFNOSUPPORT; 3134b79bda3dSEric W. Biederman if (likely(index < NEIGH_NR_TABLES)) { 31354fd3d7d9SEric W. Biederman struct neigh_table *tbl; 31364fd3d7d9SEric W. Biederman struct neighbour *neigh; 31374fd3d7d9SEric W. Biederman 3138b79bda3dSEric W. Biederman tbl = neigh_tables[index]; 31394fd3d7d9SEric W. Biederman if (!tbl) 31404fd3d7d9SEric W. Biederman goto out; 3141b560f03dSDavid Barroso rcu_read_lock_bh(); 31424b2a2bfeSDavid Ahern if (index == NEIGH_ARP_TABLE) { 31434b2a2bfeSDavid Ahern u32 key = *((u32 *)addr); 31444b2a2bfeSDavid Ahern 31454b2a2bfeSDavid Ahern neigh = __ipv4_neigh_lookup_noref(dev, key); 31464b2a2bfeSDavid Ahern } else { 31474fd3d7d9SEric W. Biederman neigh = __neigh_lookup_noref(tbl, addr, dev); 31484b2a2bfeSDavid Ahern } 31494fd3d7d9SEric W. Biederman if (!neigh) 31504fd3d7d9SEric W. Biederman neigh = __neigh_create(tbl, addr, dev, false); 31514fd3d7d9SEric W. Biederman err = PTR_ERR(neigh); 3152b560f03dSDavid Barroso if (IS_ERR(neigh)) { 3153b560f03dSDavid Barroso rcu_read_unlock_bh(); 31544fd3d7d9SEric W. Biederman goto out_kfree_skb; 3155b560f03dSDavid Barroso } 31564fd3d7d9SEric W. Biederman err = neigh->output(neigh, skb); 3157b560f03dSDavid Barroso rcu_read_unlock_bh(); 31584fd3d7d9SEric W. Biederman } 3159b79bda3dSEric W. Biederman else if (index == NEIGH_LINK_TABLE) { 3160b79bda3dSEric W. Biederman err = dev_hard_header(skb, dev, ntohs(skb->protocol), 3161b79bda3dSEric W. Biederman addr, NULL, skb->len); 3162b79bda3dSEric W. Biederman if (err < 0) 3163b79bda3dSEric W. Biederman goto out_kfree_skb; 3164b79bda3dSEric W. Biederman err = dev_queue_xmit(skb); 3165b79bda3dSEric W. Biederman } 31664fd3d7d9SEric W. Biederman out: 31674fd3d7d9SEric W. Biederman return err; 31684fd3d7d9SEric W. Biederman out_kfree_skb: 31694fd3d7d9SEric W. Biederman kfree_skb(skb); 31704fd3d7d9SEric W. Biederman goto out; 31714fd3d7d9SEric W. Biederman } 31724fd3d7d9SEric W. Biederman EXPORT_SYMBOL(neigh_xmit); 31734fd3d7d9SEric W. Biederman 31741da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 31751da177e4SLinus Torvalds 31761da177e4SLinus Torvalds static struct neighbour *neigh_get_first(struct seq_file *seq) 31771da177e4SLinus Torvalds { 31781da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 31791218854aSYOSHIFUJI Hideaki struct net *net = seq_file_net(seq); 3180d6bf7817SEric Dumazet struct neigh_hash_table *nht = state->nht; 31811da177e4SLinus Torvalds struct neighbour *n = NULL; 3182f530eed6SColin Ian King int bucket; 31831da177e4SLinus Torvalds 31841da177e4SLinus Torvalds state->flags &= ~NEIGH_SEQ_IS_PNEIGH; 3185cd089336SDavid S. Miller for (bucket = 0; bucket < (1 << nht->hash_shift); bucket++) { 3186767e97e1SEric Dumazet n = rcu_dereference_bh(nht->hash_buckets[bucket]); 31871da177e4SLinus Torvalds 31881da177e4SLinus Torvalds while (n) { 3189878628fbSYOSHIFUJI Hideaki if (!net_eq(dev_net(n->dev), net)) 3190426b5303SEric W. Biederman goto next; 31911da177e4SLinus Torvalds if (state->neigh_sub_iter) { 31921da177e4SLinus Torvalds loff_t fakep = 0; 31931da177e4SLinus Torvalds void *v; 31941da177e4SLinus Torvalds 31951da177e4SLinus Torvalds v = state->neigh_sub_iter(state, n, &fakep); 31961da177e4SLinus Torvalds if (!v) 31971da177e4SLinus Torvalds goto next; 31981da177e4SLinus Torvalds } 31991da177e4SLinus Torvalds if (!(state->flags & NEIGH_SEQ_SKIP_NOARP)) 32001da177e4SLinus Torvalds break; 3201*b071af52SEric Dumazet if (READ_ONCE(n->nud_state) & ~NUD_NOARP) 32021da177e4SLinus Torvalds break; 32031da177e4SLinus Torvalds next: 3204767e97e1SEric Dumazet n = rcu_dereference_bh(n->next); 32051da177e4SLinus Torvalds } 32061da177e4SLinus Torvalds 32071da177e4SLinus Torvalds if (n) 32081da177e4SLinus Torvalds break; 32091da177e4SLinus Torvalds } 32101da177e4SLinus Torvalds state->bucket = bucket; 32111da177e4SLinus Torvalds 32121da177e4SLinus Torvalds return n; 32131da177e4SLinus Torvalds } 32141da177e4SLinus Torvalds 32151da177e4SLinus Torvalds static struct neighbour *neigh_get_next(struct seq_file *seq, 32161da177e4SLinus Torvalds struct neighbour *n, 32171da177e4SLinus Torvalds loff_t *pos) 32181da177e4SLinus Torvalds { 32191da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 32201218854aSYOSHIFUJI Hideaki struct net *net = seq_file_net(seq); 3221d6bf7817SEric Dumazet struct neigh_hash_table *nht = state->nht; 32221da177e4SLinus Torvalds 32231da177e4SLinus Torvalds if (state->neigh_sub_iter) { 32241da177e4SLinus Torvalds void *v = state->neigh_sub_iter(state, n, pos); 32251da177e4SLinus Torvalds if (v) 32261da177e4SLinus Torvalds return n; 32271da177e4SLinus Torvalds } 3228767e97e1SEric Dumazet n = rcu_dereference_bh(n->next); 32291da177e4SLinus Torvalds 32301da177e4SLinus Torvalds while (1) { 32311da177e4SLinus Torvalds while (n) { 3232878628fbSYOSHIFUJI Hideaki if (!net_eq(dev_net(n->dev), net)) 3233426b5303SEric W. Biederman goto next; 32341da177e4SLinus Torvalds if (state->neigh_sub_iter) { 32351da177e4SLinus Torvalds void *v = state->neigh_sub_iter(state, n, pos); 32361da177e4SLinus Torvalds if (v) 32371da177e4SLinus Torvalds return n; 32381da177e4SLinus Torvalds goto next; 32391da177e4SLinus Torvalds } 32401da177e4SLinus Torvalds if (!(state->flags & NEIGH_SEQ_SKIP_NOARP)) 32411da177e4SLinus Torvalds break; 32421da177e4SLinus Torvalds 3243*b071af52SEric Dumazet if (READ_ONCE(n->nud_state) & ~NUD_NOARP) 32441da177e4SLinus Torvalds break; 32451da177e4SLinus Torvalds next: 3246767e97e1SEric Dumazet n = rcu_dereference_bh(n->next); 32471da177e4SLinus Torvalds } 32481da177e4SLinus Torvalds 32491da177e4SLinus Torvalds if (n) 32501da177e4SLinus Torvalds break; 32511da177e4SLinus Torvalds 3252cd089336SDavid S. Miller if (++state->bucket >= (1 << nht->hash_shift)) 32531da177e4SLinus Torvalds break; 32541da177e4SLinus Torvalds 3255767e97e1SEric Dumazet n = rcu_dereference_bh(nht->hash_buckets[state->bucket]); 32561da177e4SLinus Torvalds } 32571da177e4SLinus Torvalds 32581da177e4SLinus Torvalds if (n && pos) 32591da177e4SLinus Torvalds --(*pos); 32601da177e4SLinus Torvalds return n; 32611da177e4SLinus Torvalds } 32621da177e4SLinus Torvalds 32631da177e4SLinus Torvalds static struct neighbour *neigh_get_idx(struct seq_file *seq, loff_t *pos) 32641da177e4SLinus Torvalds { 32651da177e4SLinus Torvalds struct neighbour *n = neigh_get_first(seq); 32661da177e4SLinus Torvalds 32671da177e4SLinus Torvalds if (n) { 3268745e2031SChris Larson --(*pos); 32691da177e4SLinus Torvalds while (*pos) { 32701da177e4SLinus Torvalds n = neigh_get_next(seq, n, pos); 32711da177e4SLinus Torvalds if (!n) 32721da177e4SLinus Torvalds break; 32731da177e4SLinus Torvalds } 32741da177e4SLinus Torvalds } 32751da177e4SLinus Torvalds return *pos ? NULL : n; 32761da177e4SLinus Torvalds } 32771da177e4SLinus Torvalds 32781da177e4SLinus Torvalds static struct pneigh_entry *pneigh_get_first(struct seq_file *seq) 32791da177e4SLinus Torvalds { 32801da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 32811218854aSYOSHIFUJI Hideaki struct net *net = seq_file_net(seq); 32821da177e4SLinus Torvalds struct neigh_table *tbl = state->tbl; 32831da177e4SLinus Torvalds struct pneigh_entry *pn = NULL; 328448de7c0cSYang Li int bucket; 32851da177e4SLinus Torvalds 32861da177e4SLinus Torvalds state->flags |= NEIGH_SEQ_IS_PNEIGH; 32871da177e4SLinus Torvalds for (bucket = 0; bucket <= PNEIGH_HASHMASK; bucket++) { 32881da177e4SLinus Torvalds pn = tbl->phash_buckets[bucket]; 3289878628fbSYOSHIFUJI Hideaki while (pn && !net_eq(pneigh_net(pn), net)) 3290426b5303SEric W. Biederman pn = pn->next; 32911da177e4SLinus Torvalds if (pn) 32921da177e4SLinus Torvalds break; 32931da177e4SLinus Torvalds } 32941da177e4SLinus Torvalds state->bucket = bucket; 32951da177e4SLinus Torvalds 32961da177e4SLinus Torvalds return pn; 32971da177e4SLinus Torvalds } 32981da177e4SLinus Torvalds 32991da177e4SLinus Torvalds static struct pneigh_entry *pneigh_get_next(struct seq_file *seq, 33001da177e4SLinus Torvalds struct pneigh_entry *pn, 33011da177e4SLinus Torvalds loff_t *pos) 33021da177e4SLinus Torvalds { 33031da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 33041218854aSYOSHIFUJI Hideaki struct net *net = seq_file_net(seq); 33051da177e4SLinus Torvalds struct neigh_table *tbl = state->tbl; 33061da177e4SLinus Torvalds 3307df07a94cSJorge Boncompte [DTI2] do { 33081da177e4SLinus Torvalds pn = pn->next; 3309df07a94cSJorge Boncompte [DTI2] } while (pn && !net_eq(pneigh_net(pn), net)); 3310df07a94cSJorge Boncompte [DTI2] 33111da177e4SLinus Torvalds while (!pn) { 33121da177e4SLinus Torvalds if (++state->bucket > PNEIGH_HASHMASK) 33131da177e4SLinus Torvalds break; 33141da177e4SLinus Torvalds pn = tbl->phash_buckets[state->bucket]; 3315878628fbSYOSHIFUJI Hideaki while (pn && !net_eq(pneigh_net(pn), net)) 3316426b5303SEric W. Biederman pn = pn->next; 33171da177e4SLinus Torvalds if (pn) 33181da177e4SLinus Torvalds break; 33191da177e4SLinus Torvalds } 33201da177e4SLinus Torvalds 33211da177e4SLinus Torvalds if (pn && pos) 33221da177e4SLinus Torvalds --(*pos); 33231da177e4SLinus Torvalds 33241da177e4SLinus Torvalds return pn; 33251da177e4SLinus Torvalds } 33261da177e4SLinus Torvalds 33271da177e4SLinus Torvalds static struct pneigh_entry *pneigh_get_idx(struct seq_file *seq, loff_t *pos) 33281da177e4SLinus Torvalds { 33291da177e4SLinus Torvalds struct pneigh_entry *pn = pneigh_get_first(seq); 33301da177e4SLinus Torvalds 33311da177e4SLinus Torvalds if (pn) { 3332745e2031SChris Larson --(*pos); 33331da177e4SLinus Torvalds while (*pos) { 33341da177e4SLinus Torvalds pn = pneigh_get_next(seq, pn, pos); 33351da177e4SLinus Torvalds if (!pn) 33361da177e4SLinus Torvalds break; 33371da177e4SLinus Torvalds } 33381da177e4SLinus Torvalds } 33391da177e4SLinus Torvalds return *pos ? NULL : pn; 33401da177e4SLinus Torvalds } 33411da177e4SLinus Torvalds 33421da177e4SLinus Torvalds static void *neigh_get_idx_any(struct seq_file *seq, loff_t *pos) 33431da177e4SLinus Torvalds { 33441da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 33451da177e4SLinus Torvalds void *rc; 3346745e2031SChris Larson loff_t idxpos = *pos; 33471da177e4SLinus Torvalds 3348745e2031SChris Larson rc = neigh_get_idx(seq, &idxpos); 33491da177e4SLinus Torvalds if (!rc && !(state->flags & NEIGH_SEQ_NEIGH_ONLY)) 3350745e2031SChris Larson rc = pneigh_get_idx(seq, &idxpos); 33511da177e4SLinus Torvalds 33521da177e4SLinus Torvalds return rc; 33531da177e4SLinus Torvalds } 33541da177e4SLinus Torvalds 33551da177e4SLinus Torvalds void *neigh_seq_start(struct seq_file *seq, loff_t *pos, struct neigh_table *tbl, unsigned int neigh_seq_flags) 3356f3e92cb8SEric Dumazet __acquires(tbl->lock) 3357d6bf7817SEric Dumazet __acquires(rcu_bh) 33581da177e4SLinus Torvalds { 33591da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 33601da177e4SLinus Torvalds 33611da177e4SLinus Torvalds state->tbl = tbl; 33621da177e4SLinus Torvalds state->bucket = 0; 33631da177e4SLinus Torvalds state->flags = (neigh_seq_flags & ~NEIGH_SEQ_IS_PNEIGH); 33641da177e4SLinus Torvalds 3365d6bf7817SEric Dumazet rcu_read_lock_bh(); 3366d6bf7817SEric Dumazet state->nht = rcu_dereference_bh(tbl->nht); 3367f3e92cb8SEric Dumazet read_lock(&tbl->lock); 3368767e97e1SEric Dumazet 3369745e2031SChris Larson return *pos ? neigh_get_idx_any(seq, pos) : SEQ_START_TOKEN; 33701da177e4SLinus Torvalds } 33711da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_seq_start); 33721da177e4SLinus Torvalds 33731da177e4SLinus Torvalds void *neigh_seq_next(struct seq_file *seq, void *v, loff_t *pos) 33741da177e4SLinus Torvalds { 33751da177e4SLinus Torvalds struct neigh_seq_state *state; 33761da177e4SLinus Torvalds void *rc; 33771da177e4SLinus Torvalds 33781da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 3379bff69732SChris Larson rc = neigh_get_first(seq); 33801da177e4SLinus Torvalds goto out; 33811da177e4SLinus Torvalds } 33821da177e4SLinus Torvalds 33831da177e4SLinus Torvalds state = seq->private; 33841da177e4SLinus Torvalds if (!(state->flags & NEIGH_SEQ_IS_PNEIGH)) { 33851da177e4SLinus Torvalds rc = neigh_get_next(seq, v, NULL); 33861da177e4SLinus Torvalds if (rc) 33871da177e4SLinus Torvalds goto out; 33881da177e4SLinus Torvalds if (!(state->flags & NEIGH_SEQ_NEIGH_ONLY)) 33891da177e4SLinus Torvalds rc = pneigh_get_first(seq); 33901da177e4SLinus Torvalds } else { 33911da177e4SLinus Torvalds BUG_ON(state->flags & NEIGH_SEQ_NEIGH_ONLY); 33921da177e4SLinus Torvalds rc = pneigh_get_next(seq, v, NULL); 33931da177e4SLinus Torvalds } 33941da177e4SLinus Torvalds out: 33951da177e4SLinus Torvalds ++(*pos); 33961da177e4SLinus Torvalds return rc; 33971da177e4SLinus Torvalds } 33981da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_seq_next); 33991da177e4SLinus Torvalds 34001da177e4SLinus Torvalds void neigh_seq_stop(struct seq_file *seq, void *v) 3401f3e92cb8SEric Dumazet __releases(tbl->lock) 3402d6bf7817SEric Dumazet __releases(rcu_bh) 34031da177e4SLinus Torvalds { 3404f3e92cb8SEric Dumazet struct neigh_seq_state *state = seq->private; 3405f3e92cb8SEric Dumazet struct neigh_table *tbl = state->tbl; 3406f3e92cb8SEric Dumazet 3407f3e92cb8SEric Dumazet read_unlock(&tbl->lock); 3408d6bf7817SEric Dumazet rcu_read_unlock_bh(); 34091da177e4SLinus Torvalds } 34101da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_seq_stop); 34111da177e4SLinus Torvalds 34121da177e4SLinus Torvalds /* statistics via seq_file */ 34131da177e4SLinus Torvalds 34141da177e4SLinus Torvalds static void *neigh_stat_seq_start(struct seq_file *seq, loff_t *pos) 34151da177e4SLinus Torvalds { 3416359745d7SMuchun Song struct neigh_table *tbl = pde_data(file_inode(seq->file)); 34171da177e4SLinus Torvalds int cpu; 34181da177e4SLinus Torvalds 34191da177e4SLinus Torvalds if (*pos == 0) 34201da177e4SLinus Torvalds return SEQ_START_TOKEN; 34211da177e4SLinus Torvalds 34220f23174aSRusty Russell for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) { 34231da177e4SLinus Torvalds if (!cpu_possible(cpu)) 34241da177e4SLinus Torvalds continue; 34251da177e4SLinus Torvalds *pos = cpu+1; 34261da177e4SLinus Torvalds return per_cpu_ptr(tbl->stats, cpu); 34271da177e4SLinus Torvalds } 34281da177e4SLinus Torvalds return NULL; 34291da177e4SLinus Torvalds } 34301da177e4SLinus Torvalds 34311da177e4SLinus Torvalds static void *neigh_stat_seq_next(struct seq_file *seq, void *v, loff_t *pos) 34321da177e4SLinus Torvalds { 3433359745d7SMuchun Song struct neigh_table *tbl = pde_data(file_inode(seq->file)); 34341da177e4SLinus Torvalds int cpu; 34351da177e4SLinus Torvalds 34360f23174aSRusty Russell for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) { 34371da177e4SLinus Torvalds if (!cpu_possible(cpu)) 34381da177e4SLinus Torvalds continue; 34391da177e4SLinus Torvalds *pos = cpu+1; 34401da177e4SLinus Torvalds return per_cpu_ptr(tbl->stats, cpu); 34411da177e4SLinus Torvalds } 34421e3f9f07SVasily Averin (*pos)++; 34431da177e4SLinus Torvalds return NULL; 34441da177e4SLinus Torvalds } 34451da177e4SLinus Torvalds 34461da177e4SLinus Torvalds static void neigh_stat_seq_stop(struct seq_file *seq, void *v) 34471da177e4SLinus Torvalds { 34481da177e4SLinus Torvalds 34491da177e4SLinus Torvalds } 34501da177e4SLinus Torvalds 34511da177e4SLinus Torvalds static int neigh_stat_seq_show(struct seq_file *seq, void *v) 34521da177e4SLinus Torvalds { 3453359745d7SMuchun Song struct neigh_table *tbl = pde_data(file_inode(seq->file)); 34541da177e4SLinus Torvalds struct neigh_statistics *st = v; 34551da177e4SLinus Torvalds 34561da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 34570547ffe6SYajun 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"); 34581da177e4SLinus Torvalds return 0; 34591da177e4SLinus Torvalds } 34601da177e4SLinus Torvalds 34611da177e4SLinus Torvalds seq_printf(seq, "%08x %08lx %08lx %08lx %08lx %08lx %08lx " 34620547ffe6SYajun Deng "%08lx %08lx %08lx " 34630547ffe6SYajun Deng "%08lx %08lx %08lx\n", 34641da177e4SLinus Torvalds atomic_read(&tbl->entries), 34651da177e4SLinus Torvalds 34661da177e4SLinus Torvalds st->allocs, 34671da177e4SLinus Torvalds st->destroys, 34681da177e4SLinus Torvalds st->hash_grows, 34691da177e4SLinus Torvalds 34701da177e4SLinus Torvalds st->lookups, 34711da177e4SLinus Torvalds st->hits, 34721da177e4SLinus Torvalds 34731da177e4SLinus Torvalds st->res_failed, 34741da177e4SLinus Torvalds 34751da177e4SLinus Torvalds st->rcv_probes_mcast, 34761da177e4SLinus Torvalds st->rcv_probes_ucast, 34771da177e4SLinus Torvalds 34781da177e4SLinus Torvalds st->periodic_gc_runs, 34799a6d276eSNeil Horman st->forced_gc_runs, 3480fb811395SRick Jones st->unres_discards, 3481fb811395SRick Jones st->table_fulls 34821da177e4SLinus Torvalds ); 34831da177e4SLinus Torvalds 34841da177e4SLinus Torvalds return 0; 34851da177e4SLinus Torvalds } 34861da177e4SLinus Torvalds 3487f690808eSStephen Hemminger static const struct seq_operations neigh_stat_seq_ops = { 34881da177e4SLinus Torvalds .start = neigh_stat_seq_start, 34891da177e4SLinus Torvalds .next = neigh_stat_seq_next, 34901da177e4SLinus Torvalds .stop = neigh_stat_seq_stop, 34911da177e4SLinus Torvalds .show = neigh_stat_seq_show, 34921da177e4SLinus Torvalds }; 34931da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 34941da177e4SLinus Torvalds 34957b8f7a40SRoopa Prabhu static void __neigh_notify(struct neighbour *n, int type, int flags, 34967b8f7a40SRoopa Prabhu u32 pid) 34971da177e4SLinus Torvalds { 3498c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(n->dev); 34998b8aec50SThomas Graf struct sk_buff *skb; 3500b8673311SThomas Graf int err = -ENOBUFS; 35011da177e4SLinus Torvalds 3502339bf98fSThomas Graf skb = nlmsg_new(neigh_nlmsg_size(), GFP_ATOMIC); 35038b8aec50SThomas Graf if (skb == NULL) 3504b8673311SThomas Graf goto errout; 35051da177e4SLinus Torvalds 35067b8f7a40SRoopa Prabhu err = neigh_fill_info(skb, n, pid, 0, type, flags); 350726932566SPatrick McHardy if (err < 0) { 350826932566SPatrick McHardy /* -EMSGSIZE implies BUG in neigh_nlmsg_size() */ 350926932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 351026932566SPatrick McHardy kfree_skb(skb); 351126932566SPatrick McHardy goto errout; 351226932566SPatrick McHardy } 35131ce85fe4SPablo Neira Ayuso rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); 35141ce85fe4SPablo Neira Ayuso return; 3515b8673311SThomas Graf errout: 3516b8673311SThomas Graf if (err < 0) 3517426b5303SEric W. Biederman rtnl_set_sk_err(net, RTNLGRP_NEIGH, err); 3518b8673311SThomas Graf } 3519b8673311SThomas Graf 3520b8673311SThomas Graf void neigh_app_ns(struct neighbour *n) 3521b8673311SThomas Graf { 35227b8f7a40SRoopa Prabhu __neigh_notify(n, RTM_GETNEIGH, NLM_F_REQUEST, 0); 35238b8aec50SThomas Graf } 35240a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_app_ns); 35251da177e4SLinus Torvalds 35261da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 3527b93196dcSCong Wang static int unres_qlen_max = INT_MAX / SKB_TRUESIZE(ETH_FRAME_LEN); 35281da177e4SLinus Torvalds 3529fe2c6338SJoe Perches static int proc_unres_qlen(struct ctl_table *ctl, int write, 353032927393SChristoph Hellwig void *buffer, size_t *lenp, loff_t *ppos) 35318b5c171bSEric Dumazet { 35328b5c171bSEric Dumazet int size, ret; 3533fe2c6338SJoe Perches struct ctl_table tmp = *ctl; 35348b5c171bSEric Dumazet 3535eec4844fSMatteo Croce tmp.extra1 = SYSCTL_ZERO; 3536ce46cc64SShan Wei tmp.extra2 = &unres_qlen_max; 35378b5c171bSEric Dumazet tmp.data = &size; 3538ce46cc64SShan Wei 3539ce46cc64SShan Wei size = *(int *)ctl->data / SKB_TRUESIZE(ETH_FRAME_LEN); 3540ce46cc64SShan Wei ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); 3541ce46cc64SShan Wei 35428b5c171bSEric Dumazet if (write && !ret) 35438b5c171bSEric Dumazet *(int *)ctl->data = size * SKB_TRUESIZE(ETH_FRAME_LEN); 35448b5c171bSEric Dumazet return ret; 35458b5c171bSEric Dumazet } 35468b5c171bSEric Dumazet 35471d4c8c29SJiri Pirko static void neigh_copy_dflt_parms(struct net *net, struct neigh_parms *p, 35481d4c8c29SJiri Pirko int index) 35491d4c8c29SJiri Pirko { 35501d4c8c29SJiri Pirko struct net_device *dev; 35511d4c8c29SJiri Pirko int family = neigh_parms_family(p); 35521d4c8c29SJiri Pirko 35531d4c8c29SJiri Pirko rcu_read_lock(); 35541d4c8c29SJiri Pirko for_each_netdev_rcu(net, dev) { 35551d4c8c29SJiri Pirko struct neigh_parms *dst_p = 35561d4c8c29SJiri Pirko neigh_get_dev_parms_rcu(dev, family); 35571d4c8c29SJiri Pirko 35581d4c8c29SJiri Pirko if (dst_p && !test_bit(index, dst_p->data_state)) 35591d4c8c29SJiri Pirko dst_p->data[index] = p->data[index]; 35601d4c8c29SJiri Pirko } 35611d4c8c29SJiri Pirko rcu_read_unlock(); 35621d4c8c29SJiri Pirko } 35631d4c8c29SJiri Pirko 35641d4c8c29SJiri Pirko static void neigh_proc_update(struct ctl_table *ctl, int write) 35651d4c8c29SJiri Pirko { 35661d4c8c29SJiri Pirko struct net_device *dev = ctl->extra1; 35671d4c8c29SJiri Pirko struct neigh_parms *p = ctl->extra2; 356877d47afbSJiri Pirko struct net *net = neigh_parms_net(p); 35691d4c8c29SJiri Pirko int index = (int *) ctl->data - p->data; 35701d4c8c29SJiri Pirko 35711d4c8c29SJiri Pirko if (!write) 35721d4c8c29SJiri Pirko return; 35731d4c8c29SJiri Pirko 35741d4c8c29SJiri Pirko set_bit(index, p->data_state); 35757627ae60SMarcus Huewe if (index == NEIGH_VAR_DELAY_PROBE_TIME) 35762a4501aeSIdo Schimmel call_netevent_notifiers(NETEVENT_DELAY_PROBE_TIME_UPDATE, p); 35771d4c8c29SJiri Pirko if (!dev) /* NULL dev means this is default value */ 35781d4c8c29SJiri Pirko neigh_copy_dflt_parms(net, p, index); 35791d4c8c29SJiri Pirko } 35801d4c8c29SJiri Pirko 35811f9248e5SJiri Pirko static int neigh_proc_dointvec_zero_intmax(struct ctl_table *ctl, int write, 358232927393SChristoph Hellwig void *buffer, size_t *lenp, 358332927393SChristoph Hellwig loff_t *ppos) 35841f9248e5SJiri Pirko { 35851f9248e5SJiri Pirko struct ctl_table tmp = *ctl; 35861d4c8c29SJiri Pirko int ret; 35871f9248e5SJiri Pirko 3588eec4844fSMatteo Croce tmp.extra1 = SYSCTL_ZERO; 3589eec4844fSMatteo Croce tmp.extra2 = SYSCTL_INT_MAX; 35901f9248e5SJiri Pirko 35911d4c8c29SJiri Pirko ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); 35921d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 35931d4c8c29SJiri Pirko return ret; 35941f9248e5SJiri Pirko } 35951f9248e5SJiri Pirko 3596211da42eSYuwei Wang static int neigh_proc_dointvec_ms_jiffies_positive(struct ctl_table *ctl, int write, 3597211da42eSYuwei Wang void *buffer, size_t *lenp, loff_t *ppos) 3598211da42eSYuwei Wang { 3599211da42eSYuwei Wang struct ctl_table tmp = *ctl; 3600211da42eSYuwei Wang int ret; 3601211da42eSYuwei Wang 3602211da42eSYuwei Wang int min = msecs_to_jiffies(1); 3603211da42eSYuwei Wang 3604211da42eSYuwei Wang tmp.extra1 = &min; 3605211da42eSYuwei Wang tmp.extra2 = NULL; 3606211da42eSYuwei Wang 3607211da42eSYuwei Wang ret = proc_dointvec_ms_jiffies_minmax(&tmp, write, buffer, lenp, ppos); 3608211da42eSYuwei Wang neigh_proc_update(ctl, write); 3609211da42eSYuwei Wang return ret; 3610211da42eSYuwei Wang } 3611211da42eSYuwei Wang 361232927393SChristoph Hellwig int neigh_proc_dointvec(struct ctl_table *ctl, int write, void *buffer, 361332927393SChristoph Hellwig size_t *lenp, loff_t *ppos) 3614cb5b09c1SJiri Pirko { 36151d4c8c29SJiri Pirko int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 36161d4c8c29SJiri Pirko 36171d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 36181d4c8c29SJiri Pirko return ret; 3619cb5b09c1SJiri Pirko } 3620cb5b09c1SJiri Pirko EXPORT_SYMBOL(neigh_proc_dointvec); 3621cb5b09c1SJiri Pirko 362232927393SChristoph Hellwig int neigh_proc_dointvec_jiffies(struct ctl_table *ctl, int write, void *buffer, 3623cb5b09c1SJiri Pirko size_t *lenp, loff_t *ppos) 3624cb5b09c1SJiri Pirko { 36251d4c8c29SJiri Pirko int ret = proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos); 36261d4c8c29SJiri Pirko 36271d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 36281d4c8c29SJiri Pirko return ret; 3629cb5b09c1SJiri Pirko } 3630cb5b09c1SJiri Pirko EXPORT_SYMBOL(neigh_proc_dointvec_jiffies); 3631cb5b09c1SJiri Pirko 3632cb5b09c1SJiri Pirko static int neigh_proc_dointvec_userhz_jiffies(struct ctl_table *ctl, int write, 363332927393SChristoph Hellwig void *buffer, size_t *lenp, 363432927393SChristoph Hellwig loff_t *ppos) 3635cb5b09c1SJiri Pirko { 36361d4c8c29SJiri Pirko int ret = proc_dointvec_userhz_jiffies(ctl, write, buffer, lenp, ppos); 36371d4c8c29SJiri Pirko 36381d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 36391d4c8c29SJiri Pirko return ret; 3640cb5b09c1SJiri Pirko } 3641cb5b09c1SJiri Pirko 3642cb5b09c1SJiri Pirko int neigh_proc_dointvec_ms_jiffies(struct ctl_table *ctl, int write, 364332927393SChristoph Hellwig void *buffer, size_t *lenp, loff_t *ppos) 3644cb5b09c1SJiri Pirko { 36451d4c8c29SJiri Pirko int ret = proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos); 36461d4c8c29SJiri Pirko 36471d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 36481d4c8c29SJiri Pirko return ret; 3649cb5b09c1SJiri Pirko } 3650cb5b09c1SJiri Pirko EXPORT_SYMBOL(neigh_proc_dointvec_ms_jiffies); 3651cb5b09c1SJiri Pirko 3652cb5b09c1SJiri Pirko static int neigh_proc_dointvec_unres_qlen(struct ctl_table *ctl, int write, 365332927393SChristoph Hellwig void *buffer, size_t *lenp, 365432927393SChristoph Hellwig loff_t *ppos) 3655cb5b09c1SJiri Pirko { 36561d4c8c29SJiri Pirko int ret = proc_unres_qlen(ctl, write, buffer, lenp, ppos); 36571d4c8c29SJiri Pirko 36581d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 36591d4c8c29SJiri Pirko return ret; 3660cb5b09c1SJiri Pirko } 3661cb5b09c1SJiri Pirko 36624bf6980dSJean-Francois Remy static int neigh_proc_base_reachable_time(struct ctl_table *ctl, int write, 366332927393SChristoph Hellwig void *buffer, size_t *lenp, 366432927393SChristoph Hellwig loff_t *ppos) 36654bf6980dSJean-Francois Remy { 36664bf6980dSJean-Francois Remy struct neigh_parms *p = ctl->extra2; 36674bf6980dSJean-Francois Remy int ret; 36684bf6980dSJean-Francois Remy 36694bf6980dSJean-Francois Remy if (strcmp(ctl->procname, "base_reachable_time") == 0) 36704bf6980dSJean-Francois Remy ret = neigh_proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos); 36714bf6980dSJean-Francois Remy else if (strcmp(ctl->procname, "base_reachable_time_ms") == 0) 36724bf6980dSJean-Francois Remy ret = neigh_proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos); 36734bf6980dSJean-Francois Remy else 36744bf6980dSJean-Francois Remy ret = -1; 36754bf6980dSJean-Francois Remy 36764bf6980dSJean-Francois Remy if (write && ret == 0) { 36774bf6980dSJean-Francois Remy /* update reachable_time as well, otherwise, the change will 36784bf6980dSJean-Francois Remy * only be effective after the next time neigh_periodic_work 36794bf6980dSJean-Francois Remy * decides to recompute it 36804bf6980dSJean-Francois Remy */ 36814bf6980dSJean-Francois Remy p->reachable_time = 36824bf6980dSJean-Francois Remy neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); 36834bf6980dSJean-Francois Remy } 36844bf6980dSJean-Francois Remy return ret; 36854bf6980dSJean-Francois Remy } 36864bf6980dSJean-Francois Remy 36871f9248e5SJiri Pirko #define NEIGH_PARMS_DATA_OFFSET(index) \ 36881f9248e5SJiri Pirko (&((struct neigh_parms *) 0)->data[index]) 36891f9248e5SJiri Pirko 36901f9248e5SJiri Pirko #define NEIGH_SYSCTL_ENTRY(attr, data_attr, name, mval, proc) \ 36911f9248e5SJiri Pirko [NEIGH_VAR_ ## attr] = { \ 36921f9248e5SJiri Pirko .procname = name, \ 36931f9248e5SJiri Pirko .data = NEIGH_PARMS_DATA_OFFSET(NEIGH_VAR_ ## data_attr), \ 36941f9248e5SJiri Pirko .maxlen = sizeof(int), \ 36951f9248e5SJiri Pirko .mode = mval, \ 36961f9248e5SJiri Pirko .proc_handler = proc, \ 36971f9248e5SJiri Pirko } 36981f9248e5SJiri Pirko 36991f9248e5SJiri Pirko #define NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(attr, name) \ 37001f9248e5SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_zero_intmax) 37011f9248e5SJiri Pirko 37021f9248e5SJiri Pirko #define NEIGH_SYSCTL_JIFFIES_ENTRY(attr, name) \ 3703cb5b09c1SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_jiffies) 37041f9248e5SJiri Pirko 37051f9248e5SJiri Pirko #define NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(attr, name) \ 3706cb5b09c1SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_userhz_jiffies) 37071f9248e5SJiri Pirko 3708211da42eSYuwei Wang #define NEIGH_SYSCTL_MS_JIFFIES_POSITIVE_ENTRY(attr, name) \ 3709211da42eSYuwei Wang NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_ms_jiffies_positive) 3710211da42eSYuwei Wang 37111f9248e5SJiri Pirko #define NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(attr, data_attr, name) \ 3712cb5b09c1SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_ms_jiffies) 37131f9248e5SJiri Pirko 37141f9248e5SJiri Pirko #define NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(attr, data_attr, name) \ 3715cb5b09c1SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_unres_qlen) 371654716e3bSEric W. Biederman 37171da177e4SLinus Torvalds static struct neigh_sysctl_table { 37181da177e4SLinus Torvalds struct ctl_table_header *sysctl_header; 37198b5c171bSEric Dumazet struct ctl_table neigh_vars[NEIGH_VAR_MAX + 1]; 3720ab32ea5dSBrian Haley } neigh_sysctl_template __read_mostly = { 37211da177e4SLinus Torvalds .neigh_vars = { 37221f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(MCAST_PROBES, "mcast_solicit"), 37231f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(UCAST_PROBES, "ucast_solicit"), 37241f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(APP_PROBES, "app_solicit"), 37258da86466SYOSHIFUJI Hideaki/吉藤英明 NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(MCAST_REPROBES, "mcast_resolicit"), 37261f9248e5SJiri Pirko NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(RETRANS_TIME, "retrans_time"), 37271f9248e5SJiri Pirko NEIGH_SYSCTL_JIFFIES_ENTRY(BASE_REACHABLE_TIME, "base_reachable_time"), 37281f9248e5SJiri Pirko NEIGH_SYSCTL_JIFFIES_ENTRY(DELAY_PROBE_TIME, "delay_first_probe_time"), 3729211da42eSYuwei Wang NEIGH_SYSCTL_MS_JIFFIES_POSITIVE_ENTRY(INTERVAL_PROBE_TIME_MS, 3730211da42eSYuwei Wang "interval_probe_time_ms"), 37311f9248e5SJiri Pirko NEIGH_SYSCTL_JIFFIES_ENTRY(GC_STALETIME, "gc_stale_time"), 37321f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(QUEUE_LEN_BYTES, "unres_qlen_bytes"), 37331f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(PROXY_QLEN, "proxy_qlen"), 37341f9248e5SJiri Pirko NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(ANYCAST_DELAY, "anycast_delay"), 37351f9248e5SJiri Pirko NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(PROXY_DELAY, "proxy_delay"), 37361f9248e5SJiri Pirko NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(LOCKTIME, "locktime"), 37371f9248e5SJiri Pirko NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(QUEUE_LEN, QUEUE_LEN_BYTES, "unres_qlen"), 37381f9248e5SJiri Pirko NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(RETRANS_TIME_MS, RETRANS_TIME, "retrans_time_ms"), 37391f9248e5SJiri Pirko NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(BASE_REACHABLE_TIME_MS, BASE_REACHABLE_TIME, "base_reachable_time_ms"), 37408b5c171bSEric Dumazet [NEIGH_VAR_GC_INTERVAL] = { 37411da177e4SLinus Torvalds .procname = "gc_interval", 37421da177e4SLinus Torvalds .maxlen = sizeof(int), 37431da177e4SLinus Torvalds .mode = 0644, 37446d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 37451da177e4SLinus Torvalds }, 37468b5c171bSEric Dumazet [NEIGH_VAR_GC_THRESH1] = { 37471da177e4SLinus Torvalds .procname = "gc_thresh1", 37481da177e4SLinus Torvalds .maxlen = sizeof(int), 37491da177e4SLinus Torvalds .mode = 0644, 3750eec4844fSMatteo Croce .extra1 = SYSCTL_ZERO, 3751eec4844fSMatteo Croce .extra2 = SYSCTL_INT_MAX, 3752555445cdSFrancesco Fusco .proc_handler = proc_dointvec_minmax, 37531da177e4SLinus Torvalds }, 37548b5c171bSEric Dumazet [NEIGH_VAR_GC_THRESH2] = { 37551da177e4SLinus Torvalds .procname = "gc_thresh2", 37561da177e4SLinus Torvalds .maxlen = sizeof(int), 37571da177e4SLinus Torvalds .mode = 0644, 3758eec4844fSMatteo Croce .extra1 = SYSCTL_ZERO, 3759eec4844fSMatteo Croce .extra2 = SYSCTL_INT_MAX, 3760555445cdSFrancesco Fusco .proc_handler = proc_dointvec_minmax, 37611da177e4SLinus Torvalds }, 37628b5c171bSEric Dumazet [NEIGH_VAR_GC_THRESH3] = { 37631da177e4SLinus Torvalds .procname = "gc_thresh3", 37641da177e4SLinus Torvalds .maxlen = sizeof(int), 37651da177e4SLinus Torvalds .mode = 0644, 3766eec4844fSMatteo Croce .extra1 = SYSCTL_ZERO, 3767eec4844fSMatteo Croce .extra2 = SYSCTL_INT_MAX, 3768555445cdSFrancesco Fusco .proc_handler = proc_dointvec_minmax, 37691da177e4SLinus Torvalds }, 3770c3bac5a7SPavel Emelyanov {}, 37711da177e4SLinus Torvalds }, 37721da177e4SLinus Torvalds }; 37731da177e4SLinus Torvalds 37741da177e4SLinus Torvalds int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, 377573af614aSJiri Pirko proc_handler *handler) 37761da177e4SLinus Torvalds { 37771f9248e5SJiri Pirko int i; 37783c607bbbSPavel Emelyanov struct neigh_sysctl_table *t; 37791f9248e5SJiri Pirko const char *dev_name_source; 37808f40a1f9SEric W. Biederman char neigh_path[ sizeof("net//neigh/") + IFNAMSIZ + IFNAMSIZ ]; 378173af614aSJiri Pirko char *p_name; 37821da177e4SLinus Torvalds 3783425b9c7fSVasily Averin t = kmemdup(&neigh_sysctl_template, sizeof(*t), GFP_KERNEL_ACCOUNT); 37841da177e4SLinus Torvalds if (!t) 37853c607bbbSPavel Emelyanov goto err; 37863c607bbbSPavel Emelyanov 3787b194c1f1SJiri Pirko for (i = 0; i < NEIGH_VAR_GC_INTERVAL; i++) { 37881f9248e5SJiri Pirko t->neigh_vars[i].data += (long) p; 3789cb5b09c1SJiri Pirko t->neigh_vars[i].extra1 = dev; 37901d4c8c29SJiri Pirko t->neigh_vars[i].extra2 = p; 3791cb5b09c1SJiri Pirko } 37921da177e4SLinus Torvalds 37931da177e4SLinus Torvalds if (dev) { 37941da177e4SLinus Torvalds dev_name_source = dev->name; 3795d12af679SEric W. Biederman /* Terminate the table early */ 37968b5c171bSEric Dumazet memset(&t->neigh_vars[NEIGH_VAR_GC_INTERVAL], 0, 37978b5c171bSEric Dumazet sizeof(t->neigh_vars[NEIGH_VAR_GC_INTERVAL])); 37981da177e4SLinus Torvalds } else { 37999ecf07a1SMathias Krause struct neigh_table *tbl = p->tbl; 38008f40a1f9SEric W. Biederman dev_name_source = "default"; 38019ecf07a1SMathias Krause t->neigh_vars[NEIGH_VAR_GC_INTERVAL].data = &tbl->gc_interval; 38029ecf07a1SMathias Krause t->neigh_vars[NEIGH_VAR_GC_THRESH1].data = &tbl->gc_thresh1; 38039ecf07a1SMathias Krause t->neigh_vars[NEIGH_VAR_GC_THRESH2].data = &tbl->gc_thresh2; 38049ecf07a1SMathias Krause t->neigh_vars[NEIGH_VAR_GC_THRESH3].data = &tbl->gc_thresh3; 38051da177e4SLinus Torvalds } 38061da177e4SLinus Torvalds 3807f8572d8fSEric W. Biederman if (handler) { 38081da177e4SLinus Torvalds /* RetransTime */ 38098b5c171bSEric Dumazet t->neigh_vars[NEIGH_VAR_RETRANS_TIME].proc_handler = handler; 38101da177e4SLinus Torvalds /* ReachableTime */ 38118b5c171bSEric Dumazet t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].proc_handler = handler; 38121da177e4SLinus Torvalds /* RetransTime (in milliseconds)*/ 38138b5c171bSEric Dumazet t->neigh_vars[NEIGH_VAR_RETRANS_TIME_MS].proc_handler = handler; 38141da177e4SLinus Torvalds /* ReachableTime (in milliseconds) */ 38158b5c171bSEric Dumazet t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].proc_handler = handler; 38164bf6980dSJean-Francois Remy } else { 38174bf6980dSJean-Francois Remy /* Those handlers will update p->reachable_time after 38184bf6980dSJean-Francois Remy * base_reachable_time(_ms) is set to ensure the new timer starts being 38194bf6980dSJean-Francois Remy * applied after the next neighbour update instead of waiting for 38204bf6980dSJean-Francois Remy * neigh_periodic_work to update its value (can be multiple minutes) 38214bf6980dSJean-Francois Remy * So any handler that replaces them should do this as well 38224bf6980dSJean-Francois Remy */ 38234bf6980dSJean-Francois Remy /* ReachableTime */ 38244bf6980dSJean-Francois Remy t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].proc_handler = 38254bf6980dSJean-Francois Remy neigh_proc_base_reachable_time; 38264bf6980dSJean-Francois Remy /* ReachableTime (in milliseconds) */ 38274bf6980dSJean-Francois Remy t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].proc_handler = 38284bf6980dSJean-Francois Remy neigh_proc_base_reachable_time; 38291da177e4SLinus Torvalds } 38301da177e4SLinus Torvalds 383173af614aSJiri Pirko switch (neigh_parms_family(p)) { 383273af614aSJiri Pirko case AF_INET: 383373af614aSJiri Pirko p_name = "ipv4"; 383473af614aSJiri Pirko break; 383573af614aSJiri Pirko case AF_INET6: 383673af614aSJiri Pirko p_name = "ipv6"; 383773af614aSJiri Pirko break; 383873af614aSJiri Pirko default: 383973af614aSJiri Pirko BUG(); 384073af614aSJiri Pirko } 384173af614aSJiri Pirko 38428f40a1f9SEric W. Biederman snprintf(neigh_path, sizeof(neigh_path), "net/%s/neigh/%s", 38438f40a1f9SEric W. Biederman p_name, dev_name_source); 38444ab438fcSDenis V. Lunev t->sysctl_header = 38458f40a1f9SEric W. Biederman register_net_sysctl(neigh_parms_net(p), neigh_path, t->neigh_vars); 38463c607bbbSPavel Emelyanov if (!t->sysctl_header) 38478f40a1f9SEric W. Biederman goto free; 38483c607bbbSPavel Emelyanov 38491da177e4SLinus Torvalds p->sysctl_table = t; 38501da177e4SLinus Torvalds return 0; 38511da177e4SLinus Torvalds 38521da177e4SLinus Torvalds free: 38531da177e4SLinus Torvalds kfree(t); 38543c607bbbSPavel Emelyanov err: 38553c607bbbSPavel Emelyanov return -ENOBUFS; 38561da177e4SLinus Torvalds } 38570a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_sysctl_register); 38581da177e4SLinus Torvalds 38591da177e4SLinus Torvalds void neigh_sysctl_unregister(struct neigh_parms *p) 38601da177e4SLinus Torvalds { 38611da177e4SLinus Torvalds if (p->sysctl_table) { 38621da177e4SLinus Torvalds struct neigh_sysctl_table *t = p->sysctl_table; 38631da177e4SLinus Torvalds p->sysctl_table = NULL; 38645dd3df10SEric W. Biederman unregister_net_sysctl_table(t->sysctl_header); 38651da177e4SLinus Torvalds kfree(t); 38661da177e4SLinus Torvalds } 38671da177e4SLinus Torvalds } 38680a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_sysctl_unregister); 38691da177e4SLinus Torvalds 38701da177e4SLinus Torvalds #endif /* CONFIG_SYSCTL */ 38711da177e4SLinus Torvalds 3872c8822a4eSThomas Graf static int __init neigh_init(void) 3873c8822a4eSThomas Graf { 3874b97bac64SFlorian Westphal rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL, 0); 3875b97bac64SFlorian Westphal rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL, 0); 387682cbb5c6SRoopa Prabhu rtnl_register(PF_UNSPEC, RTM_GETNEIGH, neigh_get, neigh_dump_info, 0); 3877c8822a4eSThomas Graf 3878c7ac8679SGreg Rose rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info, 3879b97bac64SFlorian Westphal 0); 3880b97bac64SFlorian Westphal rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL, 0); 3881c8822a4eSThomas Graf 3882c8822a4eSThomas Graf return 0; 3883c8822a4eSThomas Graf } 3884c8822a4eSThomas Graf 3885c8822a4eSThomas Graf subsys_initcall(neigh_init); 3886