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 { 11481895a65SJason A. Donenfeld return base ? prandom_u32_max(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)) || 272e997f8a2SDavid Ahern time_after(tref, n->updated)) 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 { 292a43d8994SPavel Emelyanov neigh_hold(n); 293a43d8994SPavel Emelyanov if (unlikely(mod_timer(&n->timer, when))) { 294a43d8994SPavel Emelyanov printk("NEIGH: BUG, double timer add, state is %x\n", 295a43d8994SPavel Emelyanov n->nud_state); 296a43d8994SPavel Emelyanov dump_stack(); 297a43d8994SPavel Emelyanov } 298a43d8994SPavel Emelyanov } 299a43d8994SPavel Emelyanov 3001da177e4SLinus Torvalds static int neigh_del_timer(struct neighbour *n) 3011da177e4SLinus Torvalds { 3021da177e4SLinus Torvalds if ((n->nud_state & NUD_IN_TIMER) && 3031da177e4SLinus Torvalds del_timer(&n->timer)) { 3041da177e4SLinus Torvalds neigh_release(n); 3051da177e4SLinus Torvalds return 1; 3061da177e4SLinus Torvalds } 3071da177e4SLinus Torvalds return 0; 3081da177e4SLinus Torvalds } 3091da177e4SLinus Torvalds 310*8207f253SThomas Zeitlhofer static struct neigh_parms *neigh_get_dev_parms_rcu(struct net_device *dev, 311*8207f253SThomas Zeitlhofer int family) 312*8207f253SThomas Zeitlhofer { 313*8207f253SThomas Zeitlhofer switch (family) { 314*8207f253SThomas Zeitlhofer case AF_INET: 315*8207f253SThomas Zeitlhofer return __in_dev_arp_parms_get_rcu(dev); 316*8207f253SThomas Zeitlhofer case AF_INET6: 317*8207f253SThomas Zeitlhofer return __in6_dev_nd_parms_get_rcu(dev); 318*8207f253SThomas Zeitlhofer } 319*8207f253SThomas Zeitlhofer return NULL; 320*8207f253SThomas Zeitlhofer } 321*8207f253SThomas Zeitlhofer 322*8207f253SThomas Zeitlhofer static void neigh_parms_qlen_dec(struct net_device *dev, int family) 323*8207f253SThomas Zeitlhofer { 324*8207f253SThomas Zeitlhofer struct neigh_parms *p; 325*8207f253SThomas Zeitlhofer 326*8207f253SThomas Zeitlhofer rcu_read_lock(); 327*8207f253SThomas Zeitlhofer p = neigh_get_dev_parms_rcu(dev, family); 328*8207f253SThomas Zeitlhofer if (p) 329*8207f253SThomas Zeitlhofer p->qlen--; 330*8207f253SThomas Zeitlhofer rcu_read_unlock(); 331*8207f253SThomas Zeitlhofer } 332*8207f253SThomas Zeitlhofer 333*8207f253SThomas Zeitlhofer static void pneigh_queue_purge(struct sk_buff_head *list, struct net *net, 334*8207f253SThomas Zeitlhofer int family) 3351da177e4SLinus Torvalds { 336d5485d9dSYang Yingliang struct sk_buff_head tmp; 33766ba215cSDenis V. Lunev unsigned long flags; 3381da177e4SLinus Torvalds struct sk_buff *skb; 3391da177e4SLinus Torvalds 340d5485d9dSYang Yingliang skb_queue_head_init(&tmp); 34166ba215cSDenis V. Lunev spin_lock_irqsave(&list->lock, flags); 34266ba215cSDenis V. Lunev skb = skb_peek(list); 34366ba215cSDenis V. Lunev while (skb != NULL) { 34466ba215cSDenis V. Lunev struct sk_buff *skb_next = skb_peek_next(skb, list); 3450ff4eb3dSAlexander Mikhalitsyn struct net_device *dev = skb->dev; 346d5485d9dSYang Yingliang 3470ff4eb3dSAlexander Mikhalitsyn if (net == NULL || net_eq(dev_net(dev), net)) { 348*8207f253SThomas Zeitlhofer neigh_parms_qlen_dec(dev, family); 34966ba215cSDenis V. Lunev __skb_unlink(skb, list); 350d5485d9dSYang Yingliang __skb_queue_tail(&tmp, skb); 3511da177e4SLinus Torvalds } 35266ba215cSDenis V. Lunev skb = skb_next; 35366ba215cSDenis V. Lunev } 35466ba215cSDenis V. Lunev spin_unlock_irqrestore(&list->lock, flags); 355d5485d9dSYang Yingliang 356d5485d9dSYang Yingliang while ((skb = __skb_dequeue(&tmp))) { 357d5485d9dSYang Yingliang dev_put(skb->dev); 358d5485d9dSYang Yingliang kfree_skb(skb); 359d5485d9dSYang Yingliang } 3601da177e4SLinus Torvalds } 3611da177e4SLinus Torvalds 362859bd2efSDavid Ahern static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev, 363859bd2efSDavid Ahern bool skip_perm) 3641da177e4SLinus Torvalds { 3651da177e4SLinus Torvalds int i; 366d6bf7817SEric Dumazet struct neigh_hash_table *nht; 3671da177e4SLinus Torvalds 368d6bf7817SEric Dumazet nht = rcu_dereference_protected(tbl->nht, 369d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 370d6bf7817SEric Dumazet 371cd089336SDavid S. Miller for (i = 0; i < (1 << nht->hash_shift); i++) { 372767e97e1SEric Dumazet struct neighbour *n; 373767e97e1SEric Dumazet struct neighbour __rcu **np = &nht->hash_buckets[i]; 3741da177e4SLinus Torvalds 375767e97e1SEric Dumazet while ((n = rcu_dereference_protected(*np, 376767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))) != NULL) { 3771da177e4SLinus Torvalds if (dev && n->dev != dev) { 3781da177e4SLinus Torvalds np = &n->next; 3791da177e4SLinus Torvalds continue; 3801da177e4SLinus Torvalds } 381859bd2efSDavid Ahern if (skip_perm && n->nud_state & NUD_PERMANENT) { 382859bd2efSDavid Ahern np = &n->next; 383859bd2efSDavid Ahern continue; 384859bd2efSDavid Ahern } 385767e97e1SEric Dumazet rcu_assign_pointer(*np, 386767e97e1SEric Dumazet rcu_dereference_protected(n->next, 387767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))); 3881da177e4SLinus Torvalds write_lock(&n->lock); 3891da177e4SLinus Torvalds neigh_del_timer(n); 39058956317SDavid Ahern neigh_mark_dead(n); 3919f237430SReshetova, Elena if (refcount_read(&n->refcnt) != 1) { 3921da177e4SLinus Torvalds /* The most unpleasant situation. 3931da177e4SLinus Torvalds We must destroy neighbour entry, 3941da177e4SLinus Torvalds but someone still uses it. 3951da177e4SLinus Torvalds 3961da177e4SLinus Torvalds The destroy will be delayed until 3971da177e4SLinus Torvalds the last user releases us, but 3981da177e4SLinus Torvalds we must kill timers etc. and move 3991da177e4SLinus Torvalds it to safe state. 4001da177e4SLinus Torvalds */ 401c9ab4d85SEric Dumazet __skb_queue_purge(&n->arp_queue); 4028b5c171bSEric Dumazet n->arp_queue_len_bytes = 0; 4031da177e4SLinus Torvalds n->output = neigh_blackhole; 4041da177e4SLinus Torvalds if (n->nud_state & NUD_VALID) 4051da177e4SLinus Torvalds n->nud_state = NUD_NOARP; 4061da177e4SLinus Torvalds else 4071da177e4SLinus Torvalds n->nud_state = NUD_NONE; 408d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is stray\n", n); 4091da177e4SLinus Torvalds } 4101da177e4SLinus Torvalds write_unlock(&n->lock); 4114f494554SThomas Graf neigh_cleanup_and_release(n); 4121da177e4SLinus Torvalds } 4131da177e4SLinus Torvalds } 41449636bb1SHerbert Xu } 4151da177e4SLinus Torvalds 41649636bb1SHerbert Xu void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev) 41749636bb1SHerbert Xu { 41849636bb1SHerbert Xu write_lock_bh(&tbl->lock); 419859bd2efSDavid Ahern neigh_flush_dev(tbl, dev, false); 42049636bb1SHerbert Xu write_unlock_bh(&tbl->lock); 42149636bb1SHerbert Xu } 4220a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_changeaddr); 42349636bb1SHerbert Xu 424859bd2efSDavid Ahern static int __neigh_ifdown(struct neigh_table *tbl, struct net_device *dev, 425859bd2efSDavid Ahern bool skip_perm) 42649636bb1SHerbert Xu { 42749636bb1SHerbert Xu write_lock_bh(&tbl->lock); 428859bd2efSDavid Ahern neigh_flush_dev(tbl, dev, skip_perm); 42953b76cdfSWolfgang Bumiller pneigh_ifdown_and_unlock(tbl, dev); 430*8207f253SThomas Zeitlhofer pneigh_queue_purge(&tbl->proxy_queue, dev ? dev_net(dev) : NULL, 431*8207f253SThomas Zeitlhofer tbl->family); 43266ba215cSDenis V. Lunev if (skb_queue_empty_lockless(&tbl->proxy_queue)) 4331da177e4SLinus Torvalds del_timer_sync(&tbl->proxy_timer); 4341da177e4SLinus Torvalds return 0; 4351da177e4SLinus Torvalds } 436859bd2efSDavid Ahern 437859bd2efSDavid Ahern int neigh_carrier_down(struct neigh_table *tbl, struct net_device *dev) 438859bd2efSDavid Ahern { 439859bd2efSDavid Ahern __neigh_ifdown(tbl, dev, true); 440859bd2efSDavid Ahern return 0; 441859bd2efSDavid Ahern } 442859bd2efSDavid Ahern EXPORT_SYMBOL(neigh_carrier_down); 443859bd2efSDavid Ahern 444859bd2efSDavid Ahern int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev) 445859bd2efSDavid Ahern { 446859bd2efSDavid Ahern __neigh_ifdown(tbl, dev, false); 447859bd2efSDavid Ahern return 0; 448859bd2efSDavid Ahern } 4490a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_ifdown); 4501da177e4SLinus Torvalds 45158956317SDavid Ahern static struct neighbour *neigh_alloc(struct neigh_table *tbl, 45258956317SDavid Ahern struct net_device *dev, 4532c611ad9SRoopa Prabhu u32 flags, bool exempt_from_gc) 4541da177e4SLinus Torvalds { 4551da177e4SLinus Torvalds struct neighbour *n = NULL; 4561da177e4SLinus Torvalds unsigned long now = jiffies; 4571da177e4SLinus Torvalds int entries; 4581da177e4SLinus Torvalds 459e997f8a2SDavid Ahern if (exempt_from_gc) 46058956317SDavid Ahern goto do_alloc; 46158956317SDavid Ahern 46258956317SDavid Ahern entries = atomic_inc_return(&tbl->gc_entries) - 1; 4631da177e4SLinus Torvalds if (entries >= tbl->gc_thresh3 || 4641da177e4SLinus Torvalds (entries >= tbl->gc_thresh2 && 4651da177e4SLinus Torvalds time_after(now, tbl->last_flush + 5 * HZ))) { 4661da177e4SLinus Torvalds if (!neigh_forced_gc(tbl) && 467fb811395SRick Jones entries >= tbl->gc_thresh3) { 468fb811395SRick Jones net_info_ratelimited("%s: neighbor table overflow!\n", 469fb811395SRick Jones tbl->id); 470fb811395SRick Jones NEIGH_CACHE_STAT_INC(tbl, table_fulls); 4711da177e4SLinus Torvalds goto out_entries; 4721da177e4SLinus Torvalds } 473fb811395SRick Jones } 4741da177e4SLinus Torvalds 47558956317SDavid Ahern do_alloc: 47608433effSYOSHIFUJI Hideaki / 吉藤英明 n = kzalloc(tbl->entry_size + dev->neigh_priv_len, GFP_ATOMIC); 4771da177e4SLinus Torvalds if (!n) 4781da177e4SLinus Torvalds goto out_entries; 4791da177e4SLinus Torvalds 480c9ab4d85SEric Dumazet __skb_queue_head_init(&n->arp_queue); 4811da177e4SLinus Torvalds rwlock_init(&n->lock); 4820ed8ddf4SEric Dumazet seqlock_init(&n->ha_lock); 4831da177e4SLinus Torvalds n->updated = n->used = now; 4841da177e4SLinus Torvalds n->nud_state = NUD_NONE; 4851da177e4SLinus Torvalds n->output = neigh_blackhole; 486e4400bbfSDaniel Borkmann n->flags = flags; 487f6b72b62SDavid S. Miller seqlock_init(&n->hh.hh_lock); 4881da177e4SLinus Torvalds n->parms = neigh_parms_clone(&tbl->parms); 489e99e88a9SKees Cook timer_setup(&n->timer, neigh_timer_handler, 0); 4901da177e4SLinus Torvalds 4911da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, allocs); 4921da177e4SLinus Torvalds n->tbl = tbl; 4939f237430SReshetova, Elena refcount_set(&n->refcnt, 1); 4941da177e4SLinus Torvalds n->dead = 1; 49558956317SDavid Ahern INIT_LIST_HEAD(&n->gc_list); 4967482e384SDaniel Borkmann INIT_LIST_HEAD(&n->managed_list); 49758956317SDavid Ahern 49858956317SDavid Ahern atomic_inc(&tbl->entries); 4991da177e4SLinus Torvalds out: 5001da177e4SLinus Torvalds return n; 5011da177e4SLinus Torvalds 5021da177e4SLinus Torvalds out_entries: 503e997f8a2SDavid Ahern if (!exempt_from_gc) 50458956317SDavid Ahern atomic_dec(&tbl->gc_entries); 5051da177e4SLinus Torvalds goto out; 5061da177e4SLinus Torvalds } 5071da177e4SLinus Torvalds 5082c2aba6cSDavid S. Miller static void neigh_get_hash_rnd(u32 *x) 5092c2aba6cSDavid S. Miller { 510b3d0f789SJason A. Donenfeld *x = get_random_u32() | 1; 5112c2aba6cSDavid S. Miller } 5122c2aba6cSDavid S. Miller 513cd089336SDavid S. Miller static struct neigh_hash_table *neigh_hash_alloc(unsigned int shift) 5141da177e4SLinus Torvalds { 515cd089336SDavid S. Miller size_t size = (1 << shift) * sizeof(struct neighbour *); 516d6bf7817SEric Dumazet struct neigh_hash_table *ret; 5176193d2beSEric Dumazet struct neighbour __rcu **buckets; 5182c2aba6cSDavid S. Miller int i; 5191da177e4SLinus Torvalds 520d6bf7817SEric Dumazet ret = kmalloc(sizeof(*ret), GFP_ATOMIC); 521d6bf7817SEric Dumazet if (!ret) 522d6bf7817SEric Dumazet return NULL; 52385704cb8SKonstantin Khlebnikov if (size <= PAGE_SIZE) { 524d6bf7817SEric Dumazet buckets = kzalloc(size, GFP_ATOMIC); 52585704cb8SKonstantin Khlebnikov } else { 5266193d2beSEric Dumazet buckets = (struct neighbour __rcu **) 527d6bf7817SEric Dumazet __get_free_pages(GFP_ATOMIC | __GFP_ZERO, 528d6bf7817SEric Dumazet get_order(size)); 52901b833abSKonstantin Khlebnikov kmemleak_alloc(buckets, size, 1, GFP_ATOMIC); 53085704cb8SKonstantin Khlebnikov } 531d6bf7817SEric Dumazet if (!buckets) { 532d6bf7817SEric Dumazet kfree(ret); 533d6bf7817SEric Dumazet return NULL; 5341da177e4SLinus Torvalds } 5356193d2beSEric Dumazet ret->hash_buckets = buckets; 536cd089336SDavid S. Miller ret->hash_shift = shift; 5372c2aba6cSDavid S. Miller for (i = 0; i < NEIGH_NUM_HASH_RND; i++) 5382c2aba6cSDavid S. Miller neigh_get_hash_rnd(&ret->hash_rnd[i]); 5391da177e4SLinus Torvalds return ret; 5401da177e4SLinus Torvalds } 5411da177e4SLinus Torvalds 542d6bf7817SEric Dumazet static void neigh_hash_free_rcu(struct rcu_head *head) 5431da177e4SLinus Torvalds { 544d6bf7817SEric Dumazet struct neigh_hash_table *nht = container_of(head, 545d6bf7817SEric Dumazet struct neigh_hash_table, 546d6bf7817SEric Dumazet rcu); 547cd089336SDavid S. Miller size_t size = (1 << nht->hash_shift) * sizeof(struct neighbour *); 5486193d2beSEric Dumazet struct neighbour __rcu **buckets = nht->hash_buckets; 5491da177e4SLinus Torvalds 55085704cb8SKonstantin Khlebnikov if (size <= PAGE_SIZE) { 551d6bf7817SEric Dumazet kfree(buckets); 55285704cb8SKonstantin Khlebnikov } else { 55385704cb8SKonstantin Khlebnikov kmemleak_free(buckets); 554d6bf7817SEric Dumazet free_pages((unsigned long)buckets, get_order(size)); 55585704cb8SKonstantin Khlebnikov } 556d6bf7817SEric Dumazet kfree(nht); 5571da177e4SLinus Torvalds } 5581da177e4SLinus Torvalds 559d6bf7817SEric Dumazet static struct neigh_hash_table *neigh_hash_grow(struct neigh_table *tbl, 560cd089336SDavid S. Miller unsigned long new_shift) 5611da177e4SLinus Torvalds { 562d6bf7817SEric Dumazet unsigned int i, hash; 563d6bf7817SEric Dumazet struct neigh_hash_table *new_nht, *old_nht; 5641da177e4SLinus Torvalds 5651da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, hash_grows); 5661da177e4SLinus Torvalds 567d6bf7817SEric Dumazet old_nht = rcu_dereference_protected(tbl->nht, 568d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 569cd089336SDavid S. Miller new_nht = neigh_hash_alloc(new_shift); 570d6bf7817SEric Dumazet if (!new_nht) 571d6bf7817SEric Dumazet return old_nht; 5721da177e4SLinus Torvalds 573cd089336SDavid S. Miller for (i = 0; i < (1 << old_nht->hash_shift); i++) { 5741da177e4SLinus Torvalds struct neighbour *n, *next; 5751da177e4SLinus Torvalds 576767e97e1SEric Dumazet for (n = rcu_dereference_protected(old_nht->hash_buckets[i], 577767e97e1SEric Dumazet lockdep_is_held(&tbl->lock)); 578d6bf7817SEric Dumazet n != NULL; 579d6bf7817SEric Dumazet n = next) { 580d6bf7817SEric Dumazet hash = tbl->hash(n->primary_key, n->dev, 581d6bf7817SEric Dumazet new_nht->hash_rnd); 5821da177e4SLinus Torvalds 583cd089336SDavid S. Miller hash >>= (32 - new_nht->hash_shift); 584767e97e1SEric Dumazet next = rcu_dereference_protected(n->next, 585767e97e1SEric Dumazet lockdep_is_held(&tbl->lock)); 5861da177e4SLinus Torvalds 587767e97e1SEric Dumazet rcu_assign_pointer(n->next, 588767e97e1SEric Dumazet rcu_dereference_protected( 589767e97e1SEric Dumazet new_nht->hash_buckets[hash], 590767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))); 591767e97e1SEric Dumazet rcu_assign_pointer(new_nht->hash_buckets[hash], n); 5921da177e4SLinus Torvalds } 5931da177e4SLinus Torvalds } 5941da177e4SLinus Torvalds 595d6bf7817SEric Dumazet rcu_assign_pointer(tbl->nht, new_nht); 596d6bf7817SEric Dumazet call_rcu(&old_nht->rcu, neigh_hash_free_rcu); 597d6bf7817SEric Dumazet return new_nht; 5981da177e4SLinus Torvalds } 5991da177e4SLinus Torvalds 6001da177e4SLinus Torvalds struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey, 6011da177e4SLinus Torvalds struct net_device *dev) 6021da177e4SLinus Torvalds { 6031da177e4SLinus Torvalds struct neighbour *n; 6041da177e4SLinus Torvalds 6051da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, lookups); 6061da177e4SLinus Torvalds 607d6bf7817SEric Dumazet rcu_read_lock_bh(); 60860395a20SEric W. Biederman n = __neigh_lookup_noref(tbl, pkey, dev); 60960395a20SEric W. Biederman if (n) { 6109f237430SReshetova, Elena if (!refcount_inc_not_zero(&n->refcnt)) 611767e97e1SEric Dumazet n = NULL; 6121da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, hits); 6131da177e4SLinus Torvalds } 614767e97e1SEric Dumazet 615d6bf7817SEric Dumazet rcu_read_unlock_bh(); 6161da177e4SLinus Torvalds return n; 6171da177e4SLinus Torvalds } 6180a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_lookup); 6191da177e4SLinus Torvalds 620426b5303SEric W. Biederman struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net, 621426b5303SEric W. Biederman const void *pkey) 6221da177e4SLinus Torvalds { 6231da177e4SLinus Torvalds struct neighbour *n; 62401ccdf12SAlexey Dobriyan unsigned int key_len = tbl->key_len; 625bc4bf5f3SPavel Emelyanov u32 hash_val; 626d6bf7817SEric Dumazet struct neigh_hash_table *nht; 6271da177e4SLinus Torvalds 6281da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, lookups); 6291da177e4SLinus Torvalds 630d6bf7817SEric Dumazet rcu_read_lock_bh(); 631d6bf7817SEric Dumazet nht = rcu_dereference_bh(tbl->nht); 632cd089336SDavid S. Miller hash_val = tbl->hash(pkey, NULL, nht->hash_rnd) >> (32 - nht->hash_shift); 633767e97e1SEric Dumazet 634767e97e1SEric Dumazet for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]); 635767e97e1SEric Dumazet n != NULL; 636767e97e1SEric Dumazet n = rcu_dereference_bh(n->next)) { 637426b5303SEric W. Biederman if (!memcmp(n->primary_key, pkey, key_len) && 638878628fbSYOSHIFUJI Hideaki net_eq(dev_net(n->dev), net)) { 6399f237430SReshetova, Elena if (!refcount_inc_not_zero(&n->refcnt)) 640767e97e1SEric Dumazet n = NULL; 6411da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, hits); 6421da177e4SLinus Torvalds break; 6431da177e4SLinus Torvalds } 6441da177e4SLinus Torvalds } 645767e97e1SEric Dumazet 646d6bf7817SEric Dumazet rcu_read_unlock_bh(); 6471da177e4SLinus Torvalds return n; 6481da177e4SLinus Torvalds } 6490a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_lookup_nodev); 6501da177e4SLinus Torvalds 651e4400bbfSDaniel Borkmann static struct neighbour * 652e4400bbfSDaniel Borkmann ___neigh_create(struct neigh_table *tbl, const void *pkey, 6532c611ad9SRoopa Prabhu struct net_device *dev, u32 flags, 654e997f8a2SDavid Ahern bool exempt_from_gc, bool want_ref) 6551da177e4SLinus Torvalds { 656e4400bbfSDaniel Borkmann u32 hash_val, key_len = tbl->key_len; 657e4400bbfSDaniel Borkmann struct neighbour *n1, *rc, *n; 658d6bf7817SEric Dumazet struct neigh_hash_table *nht; 659e4400bbfSDaniel Borkmann int error; 6601da177e4SLinus Torvalds 661e4400bbfSDaniel Borkmann n = neigh_alloc(tbl, dev, flags, exempt_from_gc); 662fc651001SDavid Ahern trace_neigh_create(tbl, dev, pkey, n, exempt_from_gc); 6631da177e4SLinus Torvalds if (!n) { 6641da177e4SLinus Torvalds rc = ERR_PTR(-ENOBUFS); 6651da177e4SLinus Torvalds goto out; 6661da177e4SLinus Torvalds } 6671da177e4SLinus Torvalds 6681da177e4SLinus Torvalds memcpy(n->primary_key, pkey, key_len); 6691da177e4SLinus Torvalds n->dev = dev; 670d62607c3SJakub Kicinski netdev_hold(dev, &n->dev_tracker, GFP_ATOMIC); 6711da177e4SLinus Torvalds 6721da177e4SLinus Torvalds /* Protocol specific setup. */ 6731da177e4SLinus Torvalds if (tbl->constructor && (error = tbl->constructor(n)) < 0) { 6741da177e4SLinus Torvalds rc = ERR_PTR(error); 6751da177e4SLinus Torvalds goto out_neigh_release; 6761da177e4SLinus Torvalds } 6771da177e4SLinus Torvalds 678da6a8fa0SDavid Miller if (dev->netdev_ops->ndo_neigh_construct) { 679503eebc2SJiri Pirko error = dev->netdev_ops->ndo_neigh_construct(dev, n); 680da6a8fa0SDavid Miller if (error < 0) { 681da6a8fa0SDavid Miller rc = ERR_PTR(error); 682da6a8fa0SDavid Miller goto out_neigh_release; 683da6a8fa0SDavid Miller } 684da6a8fa0SDavid Miller } 685da6a8fa0SDavid Miller 686447f2191SDavid S. Miller /* Device specific setup. */ 687447f2191SDavid S. Miller if (n->parms->neigh_setup && 688447f2191SDavid S. Miller (error = n->parms->neigh_setup(n)) < 0) { 689447f2191SDavid S. Miller rc = ERR_PTR(error); 690447f2191SDavid S. Miller goto out_neigh_release; 691447f2191SDavid S. Miller } 692447f2191SDavid S. Miller 6931f9248e5SJiri Pirko n->confirmed = jiffies - (NEIGH_VAR(n->parms, BASE_REACHABLE_TIME) << 1); 6941da177e4SLinus Torvalds 6951da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 696d6bf7817SEric Dumazet nht = rcu_dereference_protected(tbl->nht, 697d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 6981da177e4SLinus Torvalds 699cd089336SDavid S. Miller if (atomic_read(&tbl->entries) > (1 << nht->hash_shift)) 700cd089336SDavid S. Miller nht = neigh_hash_grow(tbl, nht->hash_shift + 1); 7011da177e4SLinus Torvalds 702096b9854SJim Westfall hash_val = tbl->hash(n->primary_key, dev, nht->hash_rnd) >> (32 - nht->hash_shift); 7031da177e4SLinus Torvalds 7041da177e4SLinus Torvalds if (n->parms->dead) { 7051da177e4SLinus Torvalds rc = ERR_PTR(-EINVAL); 7061da177e4SLinus Torvalds goto out_tbl_unlock; 7071da177e4SLinus Torvalds } 7081da177e4SLinus Torvalds 709767e97e1SEric Dumazet for (n1 = rcu_dereference_protected(nht->hash_buckets[hash_val], 710767e97e1SEric Dumazet lockdep_is_held(&tbl->lock)); 711767e97e1SEric Dumazet n1 != NULL; 712767e97e1SEric Dumazet n1 = rcu_dereference_protected(n1->next, 713767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))) { 714096b9854SJim Westfall if (dev == n1->dev && !memcmp(n1->primary_key, n->primary_key, key_len)) { 715a263b309SDavid S. Miller if (want_ref) 7161da177e4SLinus Torvalds neigh_hold(n1); 7171da177e4SLinus Torvalds rc = n1; 7181da177e4SLinus Torvalds goto out_tbl_unlock; 7191da177e4SLinus Torvalds } 7201da177e4SLinus Torvalds } 7211da177e4SLinus Torvalds 7221da177e4SLinus Torvalds n->dead = 0; 723e997f8a2SDavid Ahern if (!exempt_from_gc) 7248cc196d6SDavid Ahern list_add_tail(&n->gc_list, &n->tbl->gc_list); 7257482e384SDaniel Borkmann if (n->flags & NTF_MANAGED) 7267482e384SDaniel Borkmann list_add_tail(&n->managed_list, &n->tbl->managed_list); 727a263b309SDavid S. Miller if (want_ref) 7281da177e4SLinus Torvalds neigh_hold(n); 729767e97e1SEric Dumazet rcu_assign_pointer(n->next, 730767e97e1SEric Dumazet rcu_dereference_protected(nht->hash_buckets[hash_val], 731767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))); 732767e97e1SEric Dumazet rcu_assign_pointer(nht->hash_buckets[hash_val], n); 7331da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 734d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is created\n", n); 7351da177e4SLinus Torvalds rc = n; 7361da177e4SLinus Torvalds out: 7371da177e4SLinus Torvalds return rc; 7381da177e4SLinus Torvalds out_tbl_unlock: 7391da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 7401da177e4SLinus Torvalds out_neigh_release: 74164c6f4bbSDavid Ahern if (!exempt_from_gc) 74264c6f4bbSDavid Ahern atomic_dec(&tbl->gc_entries); 7431da177e4SLinus Torvalds neigh_release(n); 7441da177e4SLinus Torvalds goto out; 7451da177e4SLinus Torvalds } 74658956317SDavid Ahern 74758956317SDavid Ahern struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey, 74858956317SDavid Ahern struct net_device *dev, bool want_ref) 74958956317SDavid Ahern { 750e4400bbfSDaniel Borkmann return ___neigh_create(tbl, pkey, dev, 0, false, want_ref); 75158956317SDavid Ahern } 752a263b309SDavid S. Miller EXPORT_SYMBOL(__neigh_create); 7531da177e4SLinus Torvalds 75401ccdf12SAlexey Dobriyan static u32 pneigh_hash(const void *pkey, unsigned int key_len) 755fa86d322SPavel Emelyanov { 756fa86d322SPavel Emelyanov u32 hash_val = *(u32 *)(pkey + key_len - 4); 757fa86d322SPavel Emelyanov hash_val ^= (hash_val >> 16); 758fa86d322SPavel Emelyanov hash_val ^= hash_val >> 8; 759fa86d322SPavel Emelyanov hash_val ^= hash_val >> 4; 760fa86d322SPavel Emelyanov hash_val &= PNEIGH_HASHMASK; 761be01d655SYOSHIFUJI Hideaki return hash_val; 762fa86d322SPavel Emelyanov } 763fa86d322SPavel Emelyanov 764be01d655SYOSHIFUJI Hideaki static struct pneigh_entry *__pneigh_lookup_1(struct pneigh_entry *n, 765be01d655SYOSHIFUJI Hideaki struct net *net, 766be01d655SYOSHIFUJI Hideaki const void *pkey, 76701ccdf12SAlexey Dobriyan unsigned int key_len, 768be01d655SYOSHIFUJI Hideaki struct net_device *dev) 769be01d655SYOSHIFUJI Hideaki { 770be01d655SYOSHIFUJI Hideaki while (n) { 771be01d655SYOSHIFUJI Hideaki if (!memcmp(n->key, pkey, key_len) && 772be01d655SYOSHIFUJI Hideaki net_eq(pneigh_net(n), net) && 773be01d655SYOSHIFUJI Hideaki (n->dev == dev || !n->dev)) 774fa86d322SPavel Emelyanov return n; 775be01d655SYOSHIFUJI Hideaki n = n->next; 776be01d655SYOSHIFUJI Hideaki } 777be01d655SYOSHIFUJI Hideaki return NULL; 778be01d655SYOSHIFUJI Hideaki } 779be01d655SYOSHIFUJI Hideaki 780be01d655SYOSHIFUJI Hideaki struct pneigh_entry *__pneigh_lookup(struct neigh_table *tbl, 781be01d655SYOSHIFUJI Hideaki struct net *net, const void *pkey, struct net_device *dev) 782be01d655SYOSHIFUJI Hideaki { 78301ccdf12SAlexey Dobriyan unsigned int key_len = tbl->key_len; 784be01d655SYOSHIFUJI Hideaki u32 hash_val = pneigh_hash(pkey, key_len); 785be01d655SYOSHIFUJI Hideaki 786be01d655SYOSHIFUJI Hideaki return __pneigh_lookup_1(tbl->phash_buckets[hash_val], 787be01d655SYOSHIFUJI Hideaki net, pkey, key_len, dev); 788fa86d322SPavel Emelyanov } 7890a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL_GPL(__pneigh_lookup); 790fa86d322SPavel Emelyanov 791426b5303SEric W. Biederman struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, 792426b5303SEric W. Biederman struct net *net, const void *pkey, 7931da177e4SLinus Torvalds struct net_device *dev, int creat) 7941da177e4SLinus Torvalds { 7951da177e4SLinus Torvalds struct pneigh_entry *n; 79601ccdf12SAlexey Dobriyan unsigned int key_len = tbl->key_len; 797be01d655SYOSHIFUJI Hideaki u32 hash_val = pneigh_hash(pkey, key_len); 7981da177e4SLinus Torvalds 7991da177e4SLinus Torvalds read_lock_bh(&tbl->lock); 800be01d655SYOSHIFUJI Hideaki n = __pneigh_lookup_1(tbl->phash_buckets[hash_val], 801be01d655SYOSHIFUJI Hideaki net, pkey, key_len, dev); 802be01d655SYOSHIFUJI Hideaki read_unlock_bh(&tbl->lock); 8031da177e4SLinus Torvalds 804be01d655SYOSHIFUJI Hideaki if (n || !creat) 8051da177e4SLinus Torvalds goto out; 8061da177e4SLinus Torvalds 8074ae28944SPavel Emelyanov ASSERT_RTNL(); 8084ae28944SPavel Emelyanov 809e195e9b5SEric Dumazet n = kzalloc(sizeof(*n) + key_len, GFP_KERNEL); 8101da177e4SLinus Torvalds if (!n) 8111da177e4SLinus Torvalds goto out; 8121da177e4SLinus Torvalds 813efd7ef1cSEric W. Biederman write_pnet(&n->net, net); 8141da177e4SLinus Torvalds memcpy(n->key, pkey, key_len); 8151da177e4SLinus Torvalds n->dev = dev; 816d62607c3SJakub Kicinski netdev_hold(dev, &n->dev_tracker, GFP_KERNEL); 8171da177e4SLinus Torvalds 8181da177e4SLinus Torvalds if (tbl->pconstructor && tbl->pconstructor(n)) { 819d62607c3SJakub Kicinski netdev_put(dev, &n->dev_tracker); 8201da177e4SLinus Torvalds kfree(n); 8211da177e4SLinus Torvalds n = NULL; 8221da177e4SLinus Torvalds goto out; 8231da177e4SLinus Torvalds } 8241da177e4SLinus Torvalds 8251da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 8261da177e4SLinus Torvalds n->next = tbl->phash_buckets[hash_val]; 8271da177e4SLinus Torvalds tbl->phash_buckets[hash_val] = n; 8281da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 8291da177e4SLinus Torvalds out: 8301da177e4SLinus Torvalds return n; 8311da177e4SLinus Torvalds } 8320a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(pneigh_lookup); 8331da177e4SLinus Torvalds 8341da177e4SLinus Torvalds 835426b5303SEric W. Biederman int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey, 8361da177e4SLinus Torvalds struct net_device *dev) 8371da177e4SLinus Torvalds { 8381da177e4SLinus Torvalds struct pneigh_entry *n, **np; 83901ccdf12SAlexey Dobriyan unsigned int key_len = tbl->key_len; 840be01d655SYOSHIFUJI Hideaki u32 hash_val = pneigh_hash(pkey, key_len); 8411da177e4SLinus Torvalds 8421da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 8431da177e4SLinus Torvalds for (np = &tbl->phash_buckets[hash_val]; (n = *np) != NULL; 8441da177e4SLinus Torvalds np = &n->next) { 845426b5303SEric W. Biederman if (!memcmp(n->key, pkey, key_len) && n->dev == dev && 846878628fbSYOSHIFUJI Hideaki net_eq(pneigh_net(n), net)) { 8471da177e4SLinus Torvalds *np = n->next; 8481da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 8491da177e4SLinus Torvalds if (tbl->pdestructor) 8501da177e4SLinus Torvalds tbl->pdestructor(n); 851d62607c3SJakub Kicinski netdev_put(n->dev, &n->dev_tracker); 8521da177e4SLinus Torvalds kfree(n); 8531da177e4SLinus Torvalds return 0; 8541da177e4SLinus Torvalds } 8551da177e4SLinus Torvalds } 8561da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 8571da177e4SLinus Torvalds return -ENOENT; 8581da177e4SLinus Torvalds } 8591da177e4SLinus Torvalds 86053b76cdfSWolfgang Bumiller static int pneigh_ifdown_and_unlock(struct neigh_table *tbl, 86153b76cdfSWolfgang Bumiller struct net_device *dev) 8621da177e4SLinus Torvalds { 86353b76cdfSWolfgang Bumiller struct pneigh_entry *n, **np, *freelist = NULL; 8641da177e4SLinus Torvalds u32 h; 8651da177e4SLinus Torvalds 8661da177e4SLinus Torvalds for (h = 0; h <= PNEIGH_HASHMASK; h++) { 8671da177e4SLinus Torvalds np = &tbl->phash_buckets[h]; 8681da177e4SLinus Torvalds while ((n = *np) != NULL) { 8691da177e4SLinus Torvalds if (!dev || n->dev == dev) { 8701da177e4SLinus Torvalds *np = n->next; 87153b76cdfSWolfgang Bumiller n->next = freelist; 87253b76cdfSWolfgang Bumiller freelist = n; 87353b76cdfSWolfgang Bumiller continue; 87453b76cdfSWolfgang Bumiller } 87553b76cdfSWolfgang Bumiller np = &n->next; 87653b76cdfSWolfgang Bumiller } 87753b76cdfSWolfgang Bumiller } 87853b76cdfSWolfgang Bumiller write_unlock_bh(&tbl->lock); 87953b76cdfSWolfgang Bumiller while ((n = freelist)) { 88053b76cdfSWolfgang Bumiller freelist = n->next; 88153b76cdfSWolfgang Bumiller n->next = NULL; 8821da177e4SLinus Torvalds if (tbl->pdestructor) 8831da177e4SLinus Torvalds tbl->pdestructor(n); 884d62607c3SJakub Kicinski netdev_put(n->dev, &n->dev_tracker); 8851da177e4SLinus Torvalds kfree(n); 8861da177e4SLinus Torvalds } 8871da177e4SLinus Torvalds return -ENOENT; 8881da177e4SLinus Torvalds } 8891da177e4SLinus Torvalds 89006f0511dSDenis V. Lunev static void neigh_parms_destroy(struct neigh_parms *parms); 89106f0511dSDenis V. Lunev 89206f0511dSDenis V. Lunev static inline void neigh_parms_put(struct neigh_parms *parms) 89306f0511dSDenis V. Lunev { 8946343944bSReshetova, Elena if (refcount_dec_and_test(&parms->refcnt)) 89506f0511dSDenis V. Lunev neigh_parms_destroy(parms); 89606f0511dSDenis V. Lunev } 8971da177e4SLinus Torvalds 8981da177e4SLinus Torvalds /* 8991da177e4SLinus Torvalds * neighbour must already be out of the table; 9001da177e4SLinus Torvalds * 9011da177e4SLinus Torvalds */ 9021da177e4SLinus Torvalds void neigh_destroy(struct neighbour *neigh) 9031da177e4SLinus Torvalds { 904da6a8fa0SDavid Miller struct net_device *dev = neigh->dev; 905da6a8fa0SDavid Miller 9061da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(neigh->tbl, destroys); 9071da177e4SLinus Torvalds 9081da177e4SLinus Torvalds if (!neigh->dead) { 909e005d193SJoe Perches pr_warn("Destroying alive neighbour %p\n", neigh); 9101da177e4SLinus Torvalds dump_stack(); 9111da177e4SLinus Torvalds return; 9121da177e4SLinus Torvalds } 9131da177e4SLinus Torvalds 9141da177e4SLinus Torvalds if (neigh_del_timer(neigh)) 915e005d193SJoe Perches pr_warn("Impossible event\n"); 9161da177e4SLinus Torvalds 917c9ab4d85SEric Dumazet write_lock_bh(&neigh->lock); 918c9ab4d85SEric Dumazet __skb_queue_purge(&neigh->arp_queue); 919c9ab4d85SEric Dumazet write_unlock_bh(&neigh->lock); 9208b5c171bSEric Dumazet neigh->arp_queue_len_bytes = 0; 9211da177e4SLinus Torvalds 922447f2191SDavid S. Miller if (dev->netdev_ops->ndo_neigh_destroy) 923503eebc2SJiri Pirko dev->netdev_ops->ndo_neigh_destroy(dev, neigh); 924447f2191SDavid S. Miller 925d62607c3SJakub Kicinski netdev_put(dev, &neigh->dev_tracker); 9261da177e4SLinus Torvalds neigh_parms_put(neigh->parms); 9271da177e4SLinus Torvalds 928d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is destroyed\n", neigh); 9291da177e4SLinus Torvalds 9301da177e4SLinus Torvalds atomic_dec(&neigh->tbl->entries); 9315b8b0060SDavid Miller kfree_rcu(neigh, rcu); 9321da177e4SLinus Torvalds } 9330a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_destroy); 9341da177e4SLinus Torvalds 9351da177e4SLinus Torvalds /* Neighbour state is suspicious; 9361da177e4SLinus Torvalds disable fast path. 9371da177e4SLinus Torvalds 9381da177e4SLinus Torvalds Called with write_locked neigh. 9391da177e4SLinus Torvalds */ 9401da177e4SLinus Torvalds static void neigh_suspect(struct neighbour *neigh) 9411da177e4SLinus Torvalds { 942d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is suspected\n", neigh); 9431da177e4SLinus Torvalds 9441da177e4SLinus Torvalds neigh->output = neigh->ops->output; 9451da177e4SLinus Torvalds } 9461da177e4SLinus Torvalds 9471da177e4SLinus Torvalds /* Neighbour state is OK; 9481da177e4SLinus Torvalds enable fast path. 9491da177e4SLinus Torvalds 9501da177e4SLinus Torvalds Called with write_locked neigh. 9511da177e4SLinus Torvalds */ 9521da177e4SLinus Torvalds static void neigh_connect(struct neighbour *neigh) 9531da177e4SLinus Torvalds { 954d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is connected\n", neigh); 9551da177e4SLinus Torvalds 9561da177e4SLinus Torvalds neigh->output = neigh->ops->connected_output; 9571da177e4SLinus Torvalds } 9581da177e4SLinus Torvalds 959e4c4e448SEric Dumazet static void neigh_periodic_work(struct work_struct *work) 9601da177e4SLinus Torvalds { 961e4c4e448SEric Dumazet struct neigh_table *tbl = container_of(work, struct neigh_table, gc_work.work); 962767e97e1SEric Dumazet struct neighbour *n; 963767e97e1SEric Dumazet struct neighbour __rcu **np; 964e4c4e448SEric Dumazet unsigned int i; 965d6bf7817SEric Dumazet struct neigh_hash_table *nht; 9661da177e4SLinus Torvalds 9671da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, periodic_gc_runs); 9681da177e4SLinus Torvalds 969e4c4e448SEric Dumazet write_lock_bh(&tbl->lock); 970d6bf7817SEric Dumazet nht = rcu_dereference_protected(tbl->nht, 971d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 9721da177e4SLinus Torvalds 9731da177e4SLinus Torvalds /* 9741da177e4SLinus Torvalds * periodically recompute ReachableTime from random function 9751da177e4SLinus Torvalds */ 9761da177e4SLinus Torvalds 977e4c4e448SEric Dumazet if (time_after(jiffies, tbl->last_rand + 300 * HZ)) { 9781da177e4SLinus Torvalds struct neigh_parms *p; 979e4c4e448SEric Dumazet tbl->last_rand = jiffies; 98075fbfd33SNicolas Dichtel list_for_each_entry(p, &tbl->parms_list, list) 9811da177e4SLinus Torvalds p->reachable_time = 9821f9248e5SJiri Pirko neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); 9831da177e4SLinus Torvalds } 9841da177e4SLinus Torvalds 985feff9ab2SDuan Jiong if (atomic_read(&tbl->entries) < tbl->gc_thresh1) 986feff9ab2SDuan Jiong goto out; 987feff9ab2SDuan Jiong 988cd089336SDavid S. Miller for (i = 0 ; i < (1 << nht->hash_shift); i++) { 989d6bf7817SEric Dumazet np = &nht->hash_buckets[i]; 9901da177e4SLinus Torvalds 991767e97e1SEric Dumazet while ((n = rcu_dereference_protected(*np, 992767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))) != NULL) { 9931da177e4SLinus Torvalds unsigned int state; 9941da177e4SLinus Torvalds 9951da177e4SLinus Torvalds write_lock(&n->lock); 9961da177e4SLinus Torvalds 9971da177e4SLinus Torvalds state = n->nud_state; 9989ce33e46SRoopa Prabhu if ((state & (NUD_PERMANENT | NUD_IN_TIMER)) || 9999ce33e46SRoopa Prabhu (n->flags & NTF_EXT_LEARNED)) { 10001da177e4SLinus Torvalds write_unlock(&n->lock); 10011da177e4SLinus Torvalds goto next_elt; 10021da177e4SLinus Torvalds } 10031da177e4SLinus Torvalds 10041da177e4SLinus Torvalds if (time_before(n->used, n->confirmed)) 10051da177e4SLinus Torvalds n->used = n->confirmed; 10061da177e4SLinus Torvalds 10079f237430SReshetova, Elena if (refcount_read(&n->refcnt) == 1 && 10081da177e4SLinus Torvalds (state == NUD_FAILED || 10091f9248e5SJiri Pirko time_after(jiffies, n->used + NEIGH_VAR(n->parms, GC_STALETIME)))) { 10101da177e4SLinus Torvalds *np = n->next; 101158956317SDavid Ahern neigh_mark_dead(n); 10121da177e4SLinus Torvalds write_unlock(&n->lock); 10134f494554SThomas Graf neigh_cleanup_and_release(n); 10141da177e4SLinus Torvalds continue; 10151da177e4SLinus Torvalds } 10161da177e4SLinus Torvalds write_unlock(&n->lock); 10171da177e4SLinus Torvalds 10181da177e4SLinus Torvalds next_elt: 10191da177e4SLinus Torvalds np = &n->next; 10201da177e4SLinus Torvalds } 1021e4c4e448SEric Dumazet /* 1022e4c4e448SEric Dumazet * It's fine to release lock here, even if hash table 1023e4c4e448SEric Dumazet * grows while we are preempted. 1024e4c4e448SEric Dumazet */ 1025e4c4e448SEric Dumazet write_unlock_bh(&tbl->lock); 1026e4c4e448SEric Dumazet cond_resched(); 1027e4c4e448SEric Dumazet write_lock_bh(&tbl->lock); 102884338a6cSMichel Machado nht = rcu_dereference_protected(tbl->nht, 102984338a6cSMichel Machado lockdep_is_held(&tbl->lock)); 1030e4c4e448SEric Dumazet } 10312724680bSYOSHIFUJI Hideaki / 吉藤英明 out: 10321f9248e5SJiri Pirko /* Cycle through all hash buckets every BASE_REACHABLE_TIME/2 ticks. 10331f9248e5SJiri Pirko * ARP entry timeouts range from 1/2 BASE_REACHABLE_TIME to 3/2 10341f9248e5SJiri Pirko * BASE_REACHABLE_TIME. 10351da177e4SLinus Torvalds */ 1036f618002bSviresh kumar queue_delayed_work(system_power_efficient_wq, &tbl->gc_work, 10371f9248e5SJiri Pirko NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME) >> 1); 1038e4c4e448SEric Dumazet write_unlock_bh(&tbl->lock); 10391da177e4SLinus Torvalds } 10401da177e4SLinus Torvalds 10411da177e4SLinus Torvalds static __inline__ int neigh_max_probes(struct neighbour *n) 10421da177e4SLinus Torvalds { 10431da177e4SLinus Torvalds struct neigh_parms *p = n->parms; 10448da86466SYOSHIFUJI Hideaki/吉藤英明 return NEIGH_VAR(p, UCAST_PROBES) + NEIGH_VAR(p, APP_PROBES) + 10458da86466SYOSHIFUJI Hideaki/吉藤英明 (n->nud_state & NUD_PROBE ? NEIGH_VAR(p, MCAST_REPROBES) : 10468da86466SYOSHIFUJI Hideaki/吉藤英明 NEIGH_VAR(p, MCAST_PROBES)); 10471da177e4SLinus Torvalds } 10481da177e4SLinus Torvalds 10495ef12d98STimo Teras static void neigh_invalidate(struct neighbour *neigh) 10500a141509SEric Dumazet __releases(neigh->lock) 10510a141509SEric Dumazet __acquires(neigh->lock) 10525ef12d98STimo Teras { 10535ef12d98STimo Teras struct sk_buff *skb; 10545ef12d98STimo Teras 10555ef12d98STimo Teras NEIGH_CACHE_STAT_INC(neigh->tbl, res_failed); 1056d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is failed\n", neigh); 10575ef12d98STimo Teras neigh->updated = jiffies; 10585ef12d98STimo Teras 10595ef12d98STimo Teras /* It is very thin place. report_unreachable is very complicated 10605ef12d98STimo Teras routine. Particularly, it can hit the same neighbour entry! 10615ef12d98STimo Teras 10625ef12d98STimo Teras So that, we try to be accurate and avoid dead loop. --ANK 10635ef12d98STimo Teras */ 10645ef12d98STimo Teras while (neigh->nud_state == NUD_FAILED && 10655ef12d98STimo Teras (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) { 10665ef12d98STimo Teras write_unlock(&neigh->lock); 10675ef12d98STimo Teras neigh->ops->error_report(neigh, skb); 10685ef12d98STimo Teras write_lock(&neigh->lock); 10695ef12d98STimo Teras } 1070c9ab4d85SEric Dumazet __skb_queue_purge(&neigh->arp_queue); 10718b5c171bSEric Dumazet neigh->arp_queue_len_bytes = 0; 10725ef12d98STimo Teras } 10735ef12d98STimo Teras 1074cd28ca0aSEric Dumazet static void neigh_probe(struct neighbour *neigh) 1075cd28ca0aSEric Dumazet __releases(neigh->lock) 1076cd28ca0aSEric Dumazet { 10774ed377e3SHannes Frederic Sowa struct sk_buff *skb = skb_peek_tail(&neigh->arp_queue); 1078cd28ca0aSEric Dumazet /* keep skb alive even if arp_queue overflows */ 1079cd28ca0aSEric Dumazet if (skb) 108019125c1aSMartin Zhang skb = skb_clone(skb, GFP_ATOMIC); 1081cd28ca0aSEric Dumazet write_unlock(&neigh->lock); 108248481c8fSEric Dumazet if (neigh->ops->solicit) 1083cd28ca0aSEric Dumazet neigh->ops->solicit(neigh, skb); 1084cd28ca0aSEric Dumazet atomic_inc(&neigh->probes); 108587fff3caSYang Wei consume_skb(skb); 1086cd28ca0aSEric Dumazet } 1087cd28ca0aSEric Dumazet 10881da177e4SLinus Torvalds /* Called when a timer expires for a neighbour entry. */ 10891da177e4SLinus Torvalds 1090e99e88a9SKees Cook static void neigh_timer_handler(struct timer_list *t) 10911da177e4SLinus Torvalds { 10921da177e4SLinus Torvalds unsigned long now, next; 1093e99e88a9SKees Cook struct neighbour *neigh = from_timer(neigh, t, timer); 109495c96174SEric Dumazet unsigned int state; 10951da177e4SLinus Torvalds int notify = 0; 10961da177e4SLinus Torvalds 10971da177e4SLinus Torvalds write_lock(&neigh->lock); 10981da177e4SLinus Torvalds 10991da177e4SLinus Torvalds state = neigh->nud_state; 11001da177e4SLinus Torvalds now = jiffies; 11011da177e4SLinus Torvalds next = now + HZ; 11021da177e4SLinus Torvalds 1103045f7b3bSDavid S. Miller if (!(state & NUD_IN_TIMER)) 11041da177e4SLinus Torvalds goto out; 11051da177e4SLinus Torvalds 11061da177e4SLinus Torvalds if (state & NUD_REACHABLE) { 11071da177e4SLinus Torvalds if (time_before_eq(now, 11081da177e4SLinus Torvalds neigh->confirmed + neigh->parms->reachable_time)) { 1109d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is still alive\n", neigh); 11101da177e4SLinus Torvalds next = neigh->confirmed + neigh->parms->reachable_time; 11111da177e4SLinus Torvalds } else if (time_before_eq(now, 11121f9248e5SJiri Pirko neigh->used + 11131f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) { 1114d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is delayed\n", neigh); 11151da177e4SLinus Torvalds neigh->nud_state = NUD_DELAY; 1116955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 11171da177e4SLinus Torvalds neigh_suspect(neigh); 11181f9248e5SJiri Pirko next = now + NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME); 11191da177e4SLinus Torvalds } else { 1120d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is suspected\n", neigh); 11211da177e4SLinus Torvalds neigh->nud_state = NUD_STALE; 1122955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 11231da177e4SLinus Torvalds neigh_suspect(neigh); 11248d71740cSTom Tucker notify = 1; 11251da177e4SLinus Torvalds } 11261da177e4SLinus Torvalds } else if (state & NUD_DELAY) { 11271da177e4SLinus Torvalds if (time_before_eq(now, 11281f9248e5SJiri Pirko neigh->confirmed + 11291f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) { 1130d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is now reachable\n", neigh); 11311da177e4SLinus Torvalds neigh->nud_state = NUD_REACHABLE; 1132955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 11331da177e4SLinus Torvalds neigh_connect(neigh); 11348d71740cSTom Tucker notify = 1; 11351da177e4SLinus Torvalds next = neigh->confirmed + neigh->parms->reachable_time; 11361da177e4SLinus Torvalds } else { 1137d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is probed\n", neigh); 11381da177e4SLinus Torvalds neigh->nud_state = NUD_PROBE; 1139955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 11401da177e4SLinus Torvalds atomic_set(&neigh->probes, 0); 1141765c9c63SErik Kline notify = 1; 114219e16d22SHangbin Liu next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), 114319e16d22SHangbin Liu HZ/100); 11441da177e4SLinus Torvalds } 11451da177e4SLinus Torvalds } else { 11461da177e4SLinus Torvalds /* NUD_PROBE|NUD_INCOMPLETE */ 114719e16d22SHangbin Liu next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), HZ/100); 11481da177e4SLinus Torvalds } 11491da177e4SLinus Torvalds 11501da177e4SLinus Torvalds if ((neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) && 11511da177e4SLinus Torvalds atomic_read(&neigh->probes) >= neigh_max_probes(neigh)) { 11521da177e4SLinus Torvalds neigh->nud_state = NUD_FAILED; 11531da177e4SLinus Torvalds notify = 1; 11545ef12d98STimo Teras neigh_invalidate(neigh); 11555e2c21dcSDuan Jiong goto out; 11561da177e4SLinus Torvalds } 11571da177e4SLinus Torvalds 11581da177e4SLinus Torvalds if (neigh->nud_state & NUD_IN_TIMER) { 115996d10d5bSHangbin Liu if (time_before(next, jiffies + HZ/100)) 116096d10d5bSHangbin Liu next = jiffies + HZ/100; 11616fb9974fSHerbert Xu if (!mod_timer(&neigh->timer, next)) 11626fb9974fSHerbert Xu neigh_hold(neigh); 11631da177e4SLinus Torvalds } 11641da177e4SLinus Torvalds if (neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) { 1165cd28ca0aSEric Dumazet neigh_probe(neigh); 11669ff56607SDavid S. Miller } else { 11671da177e4SLinus Torvalds out: 11681da177e4SLinus Torvalds write_unlock(&neigh->lock); 11699ff56607SDavid S. Miller } 11701da177e4SLinus Torvalds 1171d961db35SThomas Graf if (notify) 11727b8f7a40SRoopa Prabhu neigh_update_notify(neigh, 0); 1173d961db35SThomas Graf 117456dd18a4SRoopa Prabhu trace_neigh_timer_handler(neigh, 0); 117556dd18a4SRoopa Prabhu 11761da177e4SLinus Torvalds neigh_release(neigh); 11771da177e4SLinus Torvalds } 11781da177e4SLinus Torvalds 11794a81f6daSDaniel Borkmann int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb, 11804a81f6daSDaniel Borkmann const bool immediate_ok) 11811da177e4SLinus Torvalds { 11821da177e4SLinus Torvalds int rc; 1183cd28ca0aSEric Dumazet bool immediate_probe = false; 11841da177e4SLinus Torvalds 11851da177e4SLinus Torvalds write_lock_bh(&neigh->lock); 11861da177e4SLinus Torvalds 11871da177e4SLinus Torvalds rc = 0; 11881da177e4SLinus Torvalds if (neigh->nud_state & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE)) 11891da177e4SLinus Torvalds goto out_unlock_bh; 11902c51a97fSJulian Anastasov if (neigh->dead) 11912c51a97fSJulian Anastasov goto out_dead; 11921da177e4SLinus Torvalds 11931da177e4SLinus Torvalds if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) { 11941f9248e5SJiri Pirko if (NEIGH_VAR(neigh->parms, MCAST_PROBES) + 11951f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, APP_PROBES)) { 1196cd28ca0aSEric Dumazet unsigned long next, now = jiffies; 1197cd28ca0aSEric Dumazet 11981f9248e5SJiri Pirko atomic_set(&neigh->probes, 11991f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, UCAST_PROBES)); 1200071c3798SLorenzo Bianconi neigh_del_timer(neigh); 12011da177e4SLinus Torvalds neigh->nud_state = NUD_INCOMPLETE; 1202cd28ca0aSEric Dumazet neigh->updated = now; 12034a81f6daSDaniel Borkmann if (!immediate_ok) { 12044a81f6daSDaniel Borkmann next = now + 1; 12054a81f6daSDaniel Borkmann } else { 1206cd28ca0aSEric Dumazet immediate_probe = true; 12074a81f6daSDaniel Borkmann next = now + max(NEIGH_VAR(neigh->parms, 12084a81f6daSDaniel Borkmann RETRANS_TIME), 12094a81f6daSDaniel Borkmann HZ / 100); 12104a81f6daSDaniel Borkmann } 12114a81f6daSDaniel Borkmann neigh_add_timer(neigh, next); 12121da177e4SLinus Torvalds } else { 12131da177e4SLinus Torvalds neigh->nud_state = NUD_FAILED; 1214955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 12151da177e4SLinus Torvalds write_unlock_bh(&neigh->lock); 12161da177e4SLinus Torvalds 1217a5736eddSMenglong Dong kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_FAILED); 12181da177e4SLinus Torvalds return 1; 12191da177e4SLinus Torvalds } 12201da177e4SLinus Torvalds } else if (neigh->nud_state & NUD_STALE) { 1221d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is delayed\n", neigh); 1222071c3798SLorenzo Bianconi neigh_del_timer(neigh); 12231da177e4SLinus Torvalds neigh->nud_state = NUD_DELAY; 1224955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 12251f9248e5SJiri Pirko neigh_add_timer(neigh, jiffies + 12261f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME)); 12271da177e4SLinus Torvalds } 12281da177e4SLinus Torvalds 12291da177e4SLinus Torvalds if (neigh->nud_state == NUD_INCOMPLETE) { 12301da177e4SLinus Torvalds if (skb) { 12318b5c171bSEric Dumazet while (neigh->arp_queue_len_bytes + skb->truesize > 12321f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, QUEUE_LEN_BYTES)) { 12331da177e4SLinus Torvalds struct sk_buff *buff; 12348b5c171bSEric Dumazet 1235f72051b0SDavid S. Miller buff = __skb_dequeue(&neigh->arp_queue); 12368b5c171bSEric Dumazet if (!buff) 12378b5c171bSEric Dumazet break; 12388b5c171bSEric Dumazet neigh->arp_queue_len_bytes -= buff->truesize; 1239a5736eddSMenglong Dong kfree_skb_reason(buff, SKB_DROP_REASON_NEIGH_QUEUEFULL); 12409a6d276eSNeil Horman NEIGH_CACHE_STAT_INC(neigh->tbl, unres_discards); 12411da177e4SLinus Torvalds } 1242a4731138SEric Dumazet skb_dst_force(skb); 12431da177e4SLinus Torvalds __skb_queue_tail(&neigh->arp_queue, skb); 12448b5c171bSEric Dumazet neigh->arp_queue_len_bytes += skb->truesize; 12451da177e4SLinus Torvalds } 12461da177e4SLinus Torvalds rc = 1; 12471da177e4SLinus Torvalds } 12481da177e4SLinus Torvalds out_unlock_bh: 1249cd28ca0aSEric Dumazet if (immediate_probe) 1250cd28ca0aSEric Dumazet neigh_probe(neigh); 1251cd28ca0aSEric Dumazet else 1252cd28ca0aSEric Dumazet write_unlock(&neigh->lock); 1253cd28ca0aSEric Dumazet local_bh_enable(); 125456dd18a4SRoopa Prabhu trace_neigh_event_send_done(neigh, rc); 12551da177e4SLinus Torvalds return rc; 12562c51a97fSJulian Anastasov 12572c51a97fSJulian Anastasov out_dead: 12582c51a97fSJulian Anastasov if (neigh->nud_state & NUD_STALE) 12592c51a97fSJulian Anastasov goto out_unlock_bh; 12602c51a97fSJulian Anastasov write_unlock_bh(&neigh->lock); 1261a5736eddSMenglong Dong kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_DEAD); 126256dd18a4SRoopa Prabhu trace_neigh_event_send_dead(neigh, 1); 12632c51a97fSJulian Anastasov return 1; 12641da177e4SLinus Torvalds } 12650a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(__neigh_event_send); 12661da177e4SLinus Torvalds 1267f6b72b62SDavid S. Miller static void neigh_update_hhs(struct neighbour *neigh) 12681da177e4SLinus Torvalds { 12691da177e4SLinus Torvalds struct hh_cache *hh; 12703b04dddeSStephen Hemminger void (*update)(struct hh_cache*, const struct net_device*, const unsigned char *) 127191a72a70SDoug Kehn = NULL; 127291a72a70SDoug Kehn 127391a72a70SDoug Kehn if (neigh->dev->header_ops) 127491a72a70SDoug Kehn update = neigh->dev->header_ops->cache_update; 12751da177e4SLinus Torvalds 12761da177e4SLinus Torvalds if (update) { 1277f6b72b62SDavid S. Miller hh = &neigh->hh; 1278c305c6aeSEric Dumazet if (READ_ONCE(hh->hh_len)) { 12793644f0ceSStephen Hemminger write_seqlock_bh(&hh->hh_lock); 12801da177e4SLinus Torvalds update(hh, neigh->dev, neigh->ha); 12813644f0ceSStephen Hemminger write_sequnlock_bh(&hh->hh_lock); 12821da177e4SLinus Torvalds } 12831da177e4SLinus Torvalds } 12841da177e4SLinus Torvalds } 12851da177e4SLinus Torvalds 12861da177e4SLinus Torvalds /* Generic update routine. 12871da177e4SLinus Torvalds -- lladdr is new lladdr or NULL, if it is not supplied. 12881da177e4SLinus Torvalds -- new is new state. 12891da177e4SLinus Torvalds -- flags 12901da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE allows to override existing lladdr, 12911da177e4SLinus Torvalds if it is different. 12921da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE will suspect existing "connected" 12931da177e4SLinus Torvalds lladdr instead of overriding it 12941da177e4SLinus Torvalds if it is different. 12951da177e4SLinus Torvalds NEIGH_UPDATE_F_ADMIN means that the change is administrative. 12963dc20f47SDaniel Borkmann NEIGH_UPDATE_F_USE means that the entry is user triggered. 12977482e384SDaniel Borkmann NEIGH_UPDATE_F_MANAGED means that the entry will be auto-refreshed. 12981da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE_ISROUTER allows to override existing 12991da177e4SLinus Torvalds NTF_ROUTER flag. 13001da177e4SLinus Torvalds NEIGH_UPDATE_F_ISROUTER indicates if the neighbour is known as 13011da177e4SLinus Torvalds a router. 13021da177e4SLinus Torvalds 13031da177e4SLinus Torvalds Caller MUST hold reference count on the entry. 13041da177e4SLinus Torvalds */ 13057a35a50dSDavid Ahern static int __neigh_update(struct neighbour *neigh, const u8 *lladdr, 13067a35a50dSDavid Ahern u8 new, u32 flags, u32 nlmsg_pid, 13077a35a50dSDavid Ahern struct netlink_ext_ack *extack) 13081da177e4SLinus Torvalds { 13097482e384SDaniel Borkmann bool gc_update = false, managed_update = false; 13101da177e4SLinus Torvalds int update_isrouter = 0; 13117482e384SDaniel Borkmann struct net_device *dev; 13127482e384SDaniel Borkmann int err, notify = 0; 13137482e384SDaniel Borkmann u8 old; 13141da177e4SLinus Torvalds 131556dd18a4SRoopa Prabhu trace_neigh_update(neigh, lladdr, new, flags, nlmsg_pid); 131656dd18a4SRoopa Prabhu 13171da177e4SLinus Torvalds write_lock_bh(&neigh->lock); 13181da177e4SLinus Torvalds 13191da177e4SLinus Torvalds dev = neigh->dev; 13201da177e4SLinus Torvalds old = neigh->nud_state; 13211da177e4SLinus Torvalds err = -EPERM; 13221da177e4SLinus Torvalds 1323eb4e8facSChinmay Agarwal if (neigh->dead) { 1324eb4e8facSChinmay Agarwal NL_SET_ERR_MSG(extack, "Neighbor entry is now dead"); 1325eb4e8facSChinmay Agarwal new = old; 1326eb4e8facSChinmay Agarwal goto out; 1327eb4e8facSChinmay Agarwal } 13281da177e4SLinus Torvalds if (!(flags & NEIGH_UPDATE_F_ADMIN) && 13291da177e4SLinus Torvalds (old & (NUD_NOARP | NUD_PERMANENT))) 13301da177e4SLinus Torvalds goto out; 13311da177e4SLinus Torvalds 13327482e384SDaniel Borkmann neigh_update_flags(neigh, flags, ¬ify, &gc_update, &managed_update); 13337482e384SDaniel Borkmann if (flags & (NEIGH_UPDATE_F_USE | NEIGH_UPDATE_F_MANAGED)) { 13343dc20f47SDaniel Borkmann new = old & ~NUD_PERMANENT; 13353dc20f47SDaniel Borkmann neigh->nud_state = new; 13363dc20f47SDaniel Borkmann err = 0; 13373dc20f47SDaniel Borkmann goto out; 13383dc20f47SDaniel Borkmann } 13399ce33e46SRoopa Prabhu 13401da177e4SLinus Torvalds if (!(new & NUD_VALID)) { 13411da177e4SLinus Torvalds neigh_del_timer(neigh); 13421da177e4SLinus Torvalds if (old & NUD_CONNECTED) 13431da177e4SLinus Torvalds neigh_suspect(neigh); 13449c29a2f5SDavid Ahern neigh->nud_state = new; 13451da177e4SLinus Torvalds err = 0; 13461da177e4SLinus Torvalds notify = old & NUD_VALID; 1347d2fb4fb8SRoopa Prabhu if ((old & (NUD_INCOMPLETE | NUD_PROBE)) && 13485ef12d98STimo Teras (new & NUD_FAILED)) { 13495ef12d98STimo Teras neigh_invalidate(neigh); 13505ef12d98STimo Teras notify = 1; 13515ef12d98STimo Teras } 13521da177e4SLinus Torvalds goto out; 13531da177e4SLinus Torvalds } 13541da177e4SLinus Torvalds 13551da177e4SLinus Torvalds /* Compare new lladdr with cached one */ 13561da177e4SLinus Torvalds if (!dev->addr_len) { 13571da177e4SLinus Torvalds /* First case: device needs no address. */ 13581da177e4SLinus Torvalds lladdr = neigh->ha; 13591da177e4SLinus Torvalds } else if (lladdr) { 13601da177e4SLinus Torvalds /* The second case: if something is already cached 13611da177e4SLinus Torvalds and a new address is proposed: 13621da177e4SLinus Torvalds - compare new & old 13631da177e4SLinus Torvalds - if they are different, check override flag 13641da177e4SLinus Torvalds */ 13651da177e4SLinus Torvalds if ((old & NUD_VALID) && 13661da177e4SLinus Torvalds !memcmp(lladdr, neigh->ha, dev->addr_len)) 13671da177e4SLinus Torvalds lladdr = neigh->ha; 13681da177e4SLinus Torvalds } else { 13691da177e4SLinus Torvalds /* No address is supplied; if we know something, 13701da177e4SLinus Torvalds use it, otherwise discard the request. 13711da177e4SLinus Torvalds */ 13721da177e4SLinus Torvalds err = -EINVAL; 13737a35a50dSDavid Ahern if (!(old & NUD_VALID)) { 13747a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "No link layer address given"); 13751da177e4SLinus Torvalds goto out; 13767a35a50dSDavid Ahern } 13771da177e4SLinus Torvalds lladdr = neigh->ha; 13781da177e4SLinus Torvalds } 13791da177e4SLinus Torvalds 1380f0e0d044SVasily Khoruzhick /* Update confirmed timestamp for neighbour entry after we 1381f0e0d044SVasily Khoruzhick * received ARP packet even if it doesn't change IP to MAC binding. 1382f0e0d044SVasily Khoruzhick */ 1383f0e0d044SVasily Khoruzhick if (new & NUD_CONNECTED) 1384f0e0d044SVasily Khoruzhick neigh->confirmed = jiffies; 1385f0e0d044SVasily Khoruzhick 13861da177e4SLinus Torvalds /* If entry was valid and address is not changed, 13871da177e4SLinus Torvalds do not change entry state, if new one is STALE. 13881da177e4SLinus Torvalds */ 13891da177e4SLinus Torvalds err = 0; 13901da177e4SLinus Torvalds update_isrouter = flags & NEIGH_UPDATE_F_OVERRIDE_ISROUTER; 13911da177e4SLinus Torvalds if (old & NUD_VALID) { 13921da177e4SLinus Torvalds if (lladdr != neigh->ha && !(flags & NEIGH_UPDATE_F_OVERRIDE)) { 13931da177e4SLinus Torvalds update_isrouter = 0; 13941da177e4SLinus Torvalds if ((flags & NEIGH_UPDATE_F_WEAK_OVERRIDE) && 13951da177e4SLinus Torvalds (old & NUD_CONNECTED)) { 13961da177e4SLinus Torvalds lladdr = neigh->ha; 13971da177e4SLinus Torvalds new = NUD_STALE; 13981da177e4SLinus Torvalds } else 13991da177e4SLinus Torvalds goto out; 14001da177e4SLinus Torvalds } else { 14010e7bbcc1SJulian Anastasov if (lladdr == neigh->ha && new == NUD_STALE && 14020e7bbcc1SJulian Anastasov !(flags & NEIGH_UPDATE_F_ADMIN)) 14031da177e4SLinus Torvalds new = old; 14041da177e4SLinus Torvalds } 14051da177e4SLinus Torvalds } 14061da177e4SLinus Torvalds 1407f0e0d044SVasily Khoruzhick /* Update timestamp only once we know we will make a change to the 140877d71233SIhar Hrachyshka * neighbour entry. Otherwise we risk to move the locktime window with 140977d71233SIhar Hrachyshka * noop updates and ignore relevant ARP updates. 141077d71233SIhar Hrachyshka */ 1411f0e0d044SVasily Khoruzhick if (new != old || lladdr != neigh->ha) 141277d71233SIhar Hrachyshka neigh->updated = jiffies; 141377d71233SIhar Hrachyshka 14141da177e4SLinus Torvalds if (new != old) { 14151da177e4SLinus Torvalds neigh_del_timer(neigh); 1416765c9c63SErik Kline if (new & NUD_PROBE) 1417765c9c63SErik Kline atomic_set(&neigh->probes, 0); 1418a43d8994SPavel Emelyanov if (new & NUD_IN_TIMER) 1419667347f1SDavid S. Miller neigh_add_timer(neigh, (jiffies + 14201da177e4SLinus Torvalds ((new & NUD_REACHABLE) ? 1421667347f1SDavid S. Miller neigh->parms->reachable_time : 1422667347f1SDavid S. Miller 0))); 14239c29a2f5SDavid Ahern neigh->nud_state = new; 142453385d2dSBob Gilligan notify = 1; 14251da177e4SLinus Torvalds } 14261da177e4SLinus Torvalds 14271da177e4SLinus Torvalds if (lladdr != neigh->ha) { 14280ed8ddf4SEric Dumazet write_seqlock(&neigh->ha_lock); 14291da177e4SLinus Torvalds memcpy(&neigh->ha, lladdr, dev->addr_len); 14300ed8ddf4SEric Dumazet write_sequnlock(&neigh->ha_lock); 14311da177e4SLinus Torvalds neigh_update_hhs(neigh); 14321da177e4SLinus Torvalds if (!(new & NUD_CONNECTED)) 14331da177e4SLinus Torvalds neigh->confirmed = jiffies - 14341f9248e5SJiri Pirko (NEIGH_VAR(neigh->parms, BASE_REACHABLE_TIME) << 1); 14351da177e4SLinus Torvalds notify = 1; 14361da177e4SLinus Torvalds } 14371da177e4SLinus Torvalds if (new == old) 14381da177e4SLinus Torvalds goto out; 14391da177e4SLinus Torvalds if (new & NUD_CONNECTED) 14401da177e4SLinus Torvalds neigh_connect(neigh); 14411da177e4SLinus Torvalds else 14421da177e4SLinus Torvalds neigh_suspect(neigh); 14431da177e4SLinus Torvalds if (!(old & NUD_VALID)) { 14441da177e4SLinus Torvalds struct sk_buff *skb; 14451da177e4SLinus Torvalds 14461da177e4SLinus Torvalds /* Again: avoid dead loop if something went wrong */ 14471da177e4SLinus Torvalds 14481da177e4SLinus Torvalds while (neigh->nud_state & NUD_VALID && 14491da177e4SLinus Torvalds (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) { 145069cce1d1SDavid S. Miller struct dst_entry *dst = skb_dst(skb); 145169cce1d1SDavid S. Miller struct neighbour *n2, *n1 = neigh; 14521da177e4SLinus Torvalds write_unlock_bh(&neigh->lock); 1453e049f288Sroy.qing.li@gmail.com 1454e049f288Sroy.qing.li@gmail.com rcu_read_lock(); 145513a43d94SDavid S. Miller 145613a43d94SDavid S. Miller /* Why not just use 'neigh' as-is? The problem is that 145713a43d94SDavid S. Miller * things such as shaper, eql, and sch_teql can end up 145813a43d94SDavid S. Miller * using alternative, different, neigh objects to output 145913a43d94SDavid S. Miller * the packet in the output path. So what we need to do 146013a43d94SDavid S. Miller * here is re-lookup the top-level neigh in the path so 146113a43d94SDavid S. Miller * we can reinject the packet there. 146213a43d94SDavid S. Miller */ 146313a43d94SDavid S. Miller n2 = NULL; 1464d47ec7a0STong Zhu if (dst && dst->obsolete != DST_OBSOLETE_DEAD) { 146513a43d94SDavid S. Miller n2 = dst_neigh_lookup_skb(dst, skb); 146613a43d94SDavid S. Miller if (n2) 146769cce1d1SDavid S. Miller n1 = n2; 146813a43d94SDavid S. Miller } 14698f40b161SDavid S. Miller n1->output(n1, skb); 147013a43d94SDavid S. Miller if (n2) 147113a43d94SDavid S. Miller neigh_release(n2); 1472e049f288Sroy.qing.li@gmail.com rcu_read_unlock(); 1473e049f288Sroy.qing.li@gmail.com 14741da177e4SLinus Torvalds write_lock_bh(&neigh->lock); 14751da177e4SLinus Torvalds } 1476c9ab4d85SEric Dumazet __skb_queue_purge(&neigh->arp_queue); 14778b5c171bSEric Dumazet neigh->arp_queue_len_bytes = 0; 14781da177e4SLinus Torvalds } 14791da177e4SLinus Torvalds out: 1480fc6e8073SRoopa Prabhu if (update_isrouter) 1481fc6e8073SRoopa Prabhu neigh_update_is_router(neigh, flags, ¬ify); 14821da177e4SLinus Torvalds write_unlock_bh(&neigh->lock); 14837482e384SDaniel Borkmann if (((new ^ old) & NUD_PERMANENT) || gc_update) 14849c29a2f5SDavid Ahern neigh_update_gc_list(neigh); 14857482e384SDaniel Borkmann if (managed_update) 14867482e384SDaniel Borkmann neigh_update_managed_list(neigh); 14878d71740cSTom Tucker if (notify) 14887b8f7a40SRoopa Prabhu neigh_update_notify(neigh, nlmsg_pid); 148956dd18a4SRoopa Prabhu trace_neigh_update_done(neigh, err); 14901da177e4SLinus Torvalds return err; 14911da177e4SLinus Torvalds } 14927a35a50dSDavid Ahern 14937a35a50dSDavid Ahern int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, 14947a35a50dSDavid Ahern u32 flags, u32 nlmsg_pid) 14957a35a50dSDavid Ahern { 14967a35a50dSDavid Ahern return __neigh_update(neigh, lladdr, new, flags, nlmsg_pid, NULL); 14977a35a50dSDavid Ahern } 14980a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_update); 14991da177e4SLinus Torvalds 15007e980569SJiri Benc /* Update the neigh to listen temporarily for probe responses, even if it is 15017e980569SJiri Benc * in a NUD_FAILED state. The caller has to hold neigh->lock for writing. 15027e980569SJiri Benc */ 15037e980569SJiri Benc void __neigh_set_probe_once(struct neighbour *neigh) 15047e980569SJiri Benc { 15052c51a97fSJulian Anastasov if (neigh->dead) 15062c51a97fSJulian Anastasov return; 15077e980569SJiri Benc neigh->updated = jiffies; 15087e980569SJiri Benc if (!(neigh->nud_state & NUD_FAILED)) 15097e980569SJiri Benc return; 15102176d5d4SDuan Jiong neigh->nud_state = NUD_INCOMPLETE; 15112176d5d4SDuan Jiong atomic_set(&neigh->probes, neigh_max_probes(neigh)); 15127e980569SJiri Benc neigh_add_timer(neigh, 151319e16d22SHangbin Liu jiffies + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), 151419e16d22SHangbin Liu HZ/100)); 15157e980569SJiri Benc } 15167e980569SJiri Benc EXPORT_SYMBOL(__neigh_set_probe_once); 15177e980569SJiri Benc 15181da177e4SLinus Torvalds struct neighbour *neigh_event_ns(struct neigh_table *tbl, 15191da177e4SLinus Torvalds u8 *lladdr, void *saddr, 15201da177e4SLinus Torvalds struct net_device *dev) 15211da177e4SLinus Torvalds { 15221da177e4SLinus Torvalds struct neighbour *neigh = __neigh_lookup(tbl, saddr, dev, 15231da177e4SLinus Torvalds lladdr || !dev->addr_len); 15241da177e4SLinus Torvalds if (neigh) 15251da177e4SLinus Torvalds neigh_update(neigh, lladdr, NUD_STALE, 15267b8f7a40SRoopa Prabhu NEIGH_UPDATE_F_OVERRIDE, 0); 15271da177e4SLinus Torvalds return neigh; 15281da177e4SLinus Torvalds } 15290a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_event_ns); 15301da177e4SLinus Torvalds 153134d101ddSEric Dumazet /* called with read_lock_bh(&n->lock); */ 1532bdf53c58SEric W. Biederman static void neigh_hh_init(struct neighbour *n) 15331da177e4SLinus Torvalds { 1534bdf53c58SEric W. Biederman struct net_device *dev = n->dev; 1535bdf53c58SEric W. Biederman __be16 prot = n->tbl->protocol; 1536f6b72b62SDavid S. Miller struct hh_cache *hh = &n->hh; 15370ed8ddf4SEric Dumazet 15380ed8ddf4SEric Dumazet write_lock_bh(&n->lock); 153934d101ddSEric Dumazet 1540f6b72b62SDavid S. Miller /* Only one thread can come in here and initialize the 1541f6b72b62SDavid S. Miller * hh_cache entry. 1542f6b72b62SDavid S. Miller */ 1543b23b5455SDavid S. Miller if (!hh->hh_len) 1544b23b5455SDavid S. Miller dev->header_ops->cache(n, hh, prot); 1545f6b72b62SDavid S. Miller 15460ed8ddf4SEric Dumazet write_unlock_bh(&n->lock); 15471da177e4SLinus Torvalds } 15481da177e4SLinus Torvalds 15491da177e4SLinus Torvalds /* Slow and careful. */ 15501da177e4SLinus Torvalds 15518f40b161SDavid S. Miller int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb) 15521da177e4SLinus Torvalds { 15531da177e4SLinus Torvalds int rc = 0; 15541da177e4SLinus Torvalds 15551da177e4SLinus Torvalds if (!neigh_event_send(neigh, skb)) { 15561da177e4SLinus Torvalds int err; 15571da177e4SLinus Torvalds struct net_device *dev = neigh->dev; 15580ed8ddf4SEric Dumazet unsigned int seq; 155934d101ddSEric Dumazet 1560c305c6aeSEric Dumazet if (dev->header_ops->cache && !READ_ONCE(neigh->hh.hh_len)) 1561bdf53c58SEric W. Biederman neigh_hh_init(neigh); 156234d101ddSEric Dumazet 15630ed8ddf4SEric Dumazet do { 1564e1f16503Sramesh.nagappa@gmail.com __skb_pull(skb, skb_network_offset(skb)); 15650ed8ddf4SEric Dumazet seq = read_seqbegin(&neigh->ha_lock); 15660c4e8581SStephen Hemminger err = dev_hard_header(skb, dev, ntohs(skb->protocol), 15671da177e4SLinus Torvalds neigh->ha, NULL, skb->len); 15680ed8ddf4SEric Dumazet } while (read_seqretry(&neigh->ha_lock, seq)); 156934d101ddSEric Dumazet 15701da177e4SLinus Torvalds if (err >= 0) 1571542d4d68SDavid S. Miller rc = dev_queue_xmit(skb); 15721da177e4SLinus Torvalds else 15731da177e4SLinus Torvalds goto out_kfree_skb; 15741da177e4SLinus Torvalds } 15751da177e4SLinus Torvalds out: 15761da177e4SLinus Torvalds return rc; 15771da177e4SLinus Torvalds out_kfree_skb: 15781da177e4SLinus Torvalds rc = -EINVAL; 15791da177e4SLinus Torvalds kfree_skb(skb); 15801da177e4SLinus Torvalds goto out; 15811da177e4SLinus Torvalds } 15820a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_resolve_output); 15831da177e4SLinus Torvalds 15841da177e4SLinus Torvalds /* As fast as possible without hh cache */ 15851da177e4SLinus Torvalds 15868f40b161SDavid S. Miller int neigh_connected_output(struct neighbour *neigh, struct sk_buff *skb) 15871da177e4SLinus Torvalds { 15881da177e4SLinus Torvalds struct net_device *dev = neigh->dev; 15890ed8ddf4SEric Dumazet unsigned int seq; 15908f40b161SDavid S. Miller int err; 15911da177e4SLinus Torvalds 15920ed8ddf4SEric Dumazet do { 1593e1f16503Sramesh.nagappa@gmail.com __skb_pull(skb, skb_network_offset(skb)); 15940ed8ddf4SEric Dumazet seq = read_seqbegin(&neigh->ha_lock); 15950c4e8581SStephen Hemminger err = dev_hard_header(skb, dev, ntohs(skb->protocol), 15961da177e4SLinus Torvalds neigh->ha, NULL, skb->len); 15970ed8ddf4SEric Dumazet } while (read_seqretry(&neigh->ha_lock, seq)); 15980ed8ddf4SEric Dumazet 15991da177e4SLinus Torvalds if (err >= 0) 1600542d4d68SDavid S. Miller err = dev_queue_xmit(skb); 16011da177e4SLinus Torvalds else { 16021da177e4SLinus Torvalds err = -EINVAL; 16031da177e4SLinus Torvalds kfree_skb(skb); 16041da177e4SLinus Torvalds } 16051da177e4SLinus Torvalds return err; 16061da177e4SLinus Torvalds } 16070a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_connected_output); 16081da177e4SLinus Torvalds 16098f40b161SDavid S. Miller int neigh_direct_output(struct neighbour *neigh, struct sk_buff *skb) 16108f40b161SDavid S. Miller { 16118f40b161SDavid S. Miller return dev_queue_xmit(skb); 16128f40b161SDavid S. Miller } 16138f40b161SDavid S. Miller EXPORT_SYMBOL(neigh_direct_output); 16148f40b161SDavid S. Miller 16157482e384SDaniel Borkmann static void neigh_managed_work(struct work_struct *work) 16167482e384SDaniel Borkmann { 16177482e384SDaniel Borkmann struct neigh_table *tbl = container_of(work, struct neigh_table, 16187482e384SDaniel Borkmann managed_work.work); 16197482e384SDaniel Borkmann struct neighbour *neigh; 16207482e384SDaniel Borkmann 16217482e384SDaniel Borkmann write_lock_bh(&tbl->lock); 16227482e384SDaniel Borkmann list_for_each_entry(neigh, &tbl->managed_list, managed_list) 16234a81f6daSDaniel Borkmann neigh_event_send_probe(neigh, NULL, false); 16247482e384SDaniel Borkmann queue_delayed_work(system_power_efficient_wq, &tbl->managed_work, 1625211da42eSYuwei Wang NEIGH_VAR(&tbl->parms, INTERVAL_PROBE_TIME_MS)); 16267482e384SDaniel Borkmann write_unlock_bh(&tbl->lock); 16277482e384SDaniel Borkmann } 16287482e384SDaniel Borkmann 1629e99e88a9SKees Cook static void neigh_proxy_process(struct timer_list *t) 16301da177e4SLinus Torvalds { 1631e99e88a9SKees Cook struct neigh_table *tbl = from_timer(tbl, t, proxy_timer); 16321da177e4SLinus Torvalds long sched_next = 0; 16331da177e4SLinus Torvalds unsigned long now = jiffies; 1634f72051b0SDavid S. Miller struct sk_buff *skb, *n; 16351da177e4SLinus Torvalds 16361da177e4SLinus Torvalds spin_lock(&tbl->proxy_queue.lock); 16371da177e4SLinus Torvalds 1638f72051b0SDavid S. Miller skb_queue_walk_safe(&tbl->proxy_queue, skb, n) { 1639f72051b0SDavid S. Miller long tdif = NEIGH_CB(skb)->sched_next - now; 16401da177e4SLinus Torvalds 16411da177e4SLinus Torvalds if (tdif <= 0) { 1642f72051b0SDavid S. Miller struct net_device *dev = skb->dev; 164320e6074eSEric Dumazet 1644*8207f253SThomas Zeitlhofer neigh_parms_qlen_dec(dev, tbl->family); 1645f72051b0SDavid S. Miller __skb_unlink(skb, &tbl->proxy_queue); 16460ff4eb3dSAlexander Mikhalitsyn 164720e6074eSEric Dumazet if (tbl->proxy_redo && netif_running(dev)) { 164820e6074eSEric Dumazet rcu_read_lock(); 1649f72051b0SDavid S. Miller tbl->proxy_redo(skb); 165020e6074eSEric Dumazet rcu_read_unlock(); 165120e6074eSEric Dumazet } else { 1652f72051b0SDavid S. Miller kfree_skb(skb); 165320e6074eSEric Dumazet } 16541da177e4SLinus Torvalds 16551da177e4SLinus Torvalds dev_put(dev); 16561da177e4SLinus Torvalds } else if (!sched_next || tdif < sched_next) 16571da177e4SLinus Torvalds sched_next = tdif; 16581da177e4SLinus Torvalds } 16591da177e4SLinus Torvalds del_timer(&tbl->proxy_timer); 16601da177e4SLinus Torvalds if (sched_next) 16611da177e4SLinus Torvalds mod_timer(&tbl->proxy_timer, jiffies + sched_next); 16621da177e4SLinus Torvalds spin_unlock(&tbl->proxy_queue.lock); 16631da177e4SLinus Torvalds } 16641da177e4SLinus Torvalds 16651da177e4SLinus Torvalds void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p, 16661da177e4SLinus Torvalds struct sk_buff *skb) 16671da177e4SLinus Torvalds { 1668a533b70aSweichenchen unsigned long sched_next = jiffies + 1669a533b70aSweichenchen prandom_u32_max(NEIGH_VAR(p, PROXY_DELAY)); 16701da177e4SLinus Torvalds 16710ff4eb3dSAlexander Mikhalitsyn if (p->qlen > NEIGH_VAR(p, PROXY_QLEN)) { 16721da177e4SLinus Torvalds kfree_skb(skb); 16731da177e4SLinus Torvalds return; 16741da177e4SLinus Torvalds } 1675a61bbcf2SPatrick McHardy 1676a61bbcf2SPatrick McHardy NEIGH_CB(skb)->sched_next = sched_next; 1677a61bbcf2SPatrick McHardy NEIGH_CB(skb)->flags |= LOCALLY_ENQUEUED; 16781da177e4SLinus Torvalds 16791da177e4SLinus Torvalds spin_lock(&tbl->proxy_queue.lock); 16801da177e4SLinus Torvalds if (del_timer(&tbl->proxy_timer)) { 16811da177e4SLinus Torvalds if (time_before(tbl->proxy_timer.expires, sched_next)) 16821da177e4SLinus Torvalds sched_next = tbl->proxy_timer.expires; 16831da177e4SLinus Torvalds } 1684adf30907SEric Dumazet skb_dst_drop(skb); 16851da177e4SLinus Torvalds dev_hold(skb->dev); 16861da177e4SLinus Torvalds __skb_queue_tail(&tbl->proxy_queue, skb); 16870ff4eb3dSAlexander Mikhalitsyn p->qlen++; 16881da177e4SLinus Torvalds mod_timer(&tbl->proxy_timer, sched_next); 16891da177e4SLinus Torvalds spin_unlock(&tbl->proxy_queue.lock); 16901da177e4SLinus Torvalds } 16910a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(pneigh_enqueue); 16921da177e4SLinus Torvalds 169397fd5bc7STobias Klauser static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl, 1694426b5303SEric W. Biederman struct net *net, int ifindex) 1695426b5303SEric W. Biederman { 1696426b5303SEric W. Biederman struct neigh_parms *p; 1697426b5303SEric W. Biederman 169875fbfd33SNicolas Dichtel list_for_each_entry(p, &tbl->parms_list, list) { 1699878628fbSYOSHIFUJI Hideaki if ((p->dev && p->dev->ifindex == ifindex && net_eq(neigh_parms_net(p), net)) || 1700170d6f99SGao feng (!p->dev && !ifindex && net_eq(net, &init_net))) 1701426b5303SEric W. Biederman return p; 1702426b5303SEric W. Biederman } 1703426b5303SEric W. Biederman 1704426b5303SEric W. Biederman return NULL; 1705426b5303SEric W. Biederman } 17061da177e4SLinus Torvalds 17071da177e4SLinus Torvalds struct neigh_parms *neigh_parms_alloc(struct net_device *dev, 17081da177e4SLinus Torvalds struct neigh_table *tbl) 17091da177e4SLinus Torvalds { 1710cf89d6b2SGao feng struct neigh_parms *p; 171100829823SStephen Hemminger struct net *net = dev_net(dev); 171200829823SStephen Hemminger const struct net_device_ops *ops = dev->netdev_ops; 17131da177e4SLinus Torvalds 1714cf89d6b2SGao feng p = kmemdup(&tbl->parms, sizeof(*p), GFP_KERNEL); 17151da177e4SLinus Torvalds if (p) { 17161da177e4SLinus Torvalds p->tbl = tbl; 17176343944bSReshetova, Elena refcount_set(&p->refcnt, 1); 17181da177e4SLinus Torvalds p->reachable_time = 17191f9248e5SJiri Pirko neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); 17200ff4eb3dSAlexander Mikhalitsyn p->qlen = 0; 1721d62607c3SJakub Kicinski netdev_hold(dev, &p->dev_tracker, GFP_KERNEL); 1722c7fb64dbSThomas Graf p->dev = dev; 1723efd7ef1cSEric W. Biederman write_pnet(&p->net, net); 17241da177e4SLinus Torvalds p->sysctl_table = NULL; 172563134803SVeaceslav Falico 172663134803SVeaceslav Falico if (ops->ndo_neigh_setup && ops->ndo_neigh_setup(dev, p)) { 1727d62607c3SJakub Kicinski netdev_put(dev, &p->dev_tracker); 172863134803SVeaceslav Falico kfree(p); 172963134803SVeaceslav Falico return NULL; 173063134803SVeaceslav Falico } 173163134803SVeaceslav Falico 17321da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 173375fbfd33SNicolas Dichtel list_add(&p->list, &tbl->parms.list); 17341da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 17351d4c8c29SJiri Pirko 17361d4c8c29SJiri Pirko neigh_parms_data_state_cleanall(p); 17371da177e4SLinus Torvalds } 17381da177e4SLinus Torvalds return p; 17391da177e4SLinus Torvalds } 17400a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_parms_alloc); 17411da177e4SLinus Torvalds 17421da177e4SLinus Torvalds static void neigh_rcu_free_parms(struct rcu_head *head) 17431da177e4SLinus Torvalds { 17441da177e4SLinus Torvalds struct neigh_parms *parms = 17451da177e4SLinus Torvalds container_of(head, struct neigh_parms, rcu_head); 17461da177e4SLinus Torvalds 17471da177e4SLinus Torvalds neigh_parms_put(parms); 17481da177e4SLinus Torvalds } 17491da177e4SLinus Torvalds 17501da177e4SLinus Torvalds void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms) 17511da177e4SLinus Torvalds { 17521da177e4SLinus Torvalds if (!parms || parms == &tbl->parms) 17531da177e4SLinus Torvalds return; 17541da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 175575fbfd33SNicolas Dichtel list_del(&parms->list); 17561da177e4SLinus Torvalds parms->dead = 1; 17571da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 1758d62607c3SJakub Kicinski netdev_put(parms->dev, &parms->dev_tracker); 17591da177e4SLinus Torvalds call_rcu(&parms->rcu_head, neigh_rcu_free_parms); 17601da177e4SLinus Torvalds } 17610a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_parms_release); 17621da177e4SLinus Torvalds 176306f0511dSDenis V. Lunev static void neigh_parms_destroy(struct neigh_parms *parms) 17641da177e4SLinus Torvalds { 17651da177e4SLinus Torvalds kfree(parms); 17661da177e4SLinus Torvalds } 17671da177e4SLinus Torvalds 1768c2ecba71SPavel Emelianov static struct lock_class_key neigh_table_proxy_queue_class; 1769c2ecba71SPavel Emelianov 1770d7480fd3SWANG Cong static struct neigh_table *neigh_tables[NEIGH_NR_TABLES] __read_mostly; 1771d7480fd3SWANG Cong 1772d7480fd3SWANG Cong void neigh_table_init(int index, struct neigh_table *tbl) 17731da177e4SLinus Torvalds { 17741da177e4SLinus Torvalds unsigned long now = jiffies; 17751da177e4SLinus Torvalds unsigned long phsize; 17761da177e4SLinus Torvalds 177775fbfd33SNicolas Dichtel INIT_LIST_HEAD(&tbl->parms_list); 177858956317SDavid Ahern INIT_LIST_HEAD(&tbl->gc_list); 17797482e384SDaniel Borkmann INIT_LIST_HEAD(&tbl->managed_list); 17807482e384SDaniel Borkmann 178175fbfd33SNicolas Dichtel list_add(&tbl->parms.list, &tbl->parms_list); 1782e42ea986SEric Dumazet write_pnet(&tbl->parms.net, &init_net); 17836343944bSReshetova, Elena refcount_set(&tbl->parms.refcnt, 1); 17841da177e4SLinus Torvalds tbl->parms.reachable_time = 17851f9248e5SJiri Pirko neigh_rand_reach_time(NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME)); 17860ff4eb3dSAlexander Mikhalitsyn tbl->parms.qlen = 0; 17871da177e4SLinus Torvalds 17881da177e4SLinus Torvalds tbl->stats = alloc_percpu(struct neigh_statistics); 17891da177e4SLinus Torvalds if (!tbl->stats) 17901da177e4SLinus Torvalds panic("cannot create neighbour cache statistics"); 17911da177e4SLinus Torvalds 17921da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 179371a5053aSChristoph Hellwig if (!proc_create_seq_data(tbl->id, 0, init_net.proc_net_stat, 179471a5053aSChristoph Hellwig &neigh_stat_seq_ops, tbl)) 17951da177e4SLinus Torvalds panic("cannot create neighbour proc dir entry"); 17961da177e4SLinus Torvalds #endif 17971da177e4SLinus Torvalds 1798cd089336SDavid S. Miller RCU_INIT_POINTER(tbl->nht, neigh_hash_alloc(3)); 17991da177e4SLinus Torvalds 18001da177e4SLinus Torvalds phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *); 180177d04bd9SAndrew Morton tbl->phash_buckets = kzalloc(phsize, GFP_KERNEL); 18021da177e4SLinus Torvalds 1803d6bf7817SEric Dumazet if (!tbl->nht || !tbl->phash_buckets) 18041da177e4SLinus Torvalds panic("cannot allocate neighbour cache hashes"); 18051da177e4SLinus Torvalds 180608433effSYOSHIFUJI Hideaki / 吉藤英明 if (!tbl->entry_size) 180708433effSYOSHIFUJI Hideaki / 吉藤英明 tbl->entry_size = ALIGN(offsetof(struct neighbour, primary_key) + 180808433effSYOSHIFUJI Hideaki / 吉藤英明 tbl->key_len, NEIGH_PRIV_ALIGN); 180908433effSYOSHIFUJI Hideaki / 吉藤英明 else 181008433effSYOSHIFUJI Hideaki / 吉藤英明 WARN_ON(tbl->entry_size % NEIGH_PRIV_ALIGN); 181108433effSYOSHIFUJI Hideaki / 吉藤英明 18121da177e4SLinus Torvalds rwlock_init(&tbl->lock); 18137482e384SDaniel Borkmann 1814203b42f7STejun Heo INIT_DEFERRABLE_WORK(&tbl->gc_work, neigh_periodic_work); 1815f618002bSviresh kumar queue_delayed_work(system_power_efficient_wq, &tbl->gc_work, 1816f618002bSviresh kumar tbl->parms.reachable_time); 18177482e384SDaniel Borkmann INIT_DEFERRABLE_WORK(&tbl->managed_work, neigh_managed_work); 18187482e384SDaniel Borkmann queue_delayed_work(system_power_efficient_wq, &tbl->managed_work, 0); 18197482e384SDaniel Borkmann 1820e99e88a9SKees Cook timer_setup(&tbl->proxy_timer, neigh_proxy_process, 0); 1821c2ecba71SPavel Emelianov skb_queue_head_init_class(&tbl->proxy_queue, 1822c2ecba71SPavel Emelianov &neigh_table_proxy_queue_class); 18231da177e4SLinus Torvalds 18241da177e4SLinus Torvalds tbl->last_flush = now; 18251da177e4SLinus Torvalds tbl->last_rand = now + tbl->parms.reachable_time * 20; 1826bd89efc5SSimon Kelley 1827d7480fd3SWANG Cong neigh_tables[index] = tbl; 18281da177e4SLinus Torvalds } 18290a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_table_init); 18301da177e4SLinus Torvalds 1831d7480fd3SWANG Cong int neigh_table_clear(int index, struct neigh_table *tbl) 18321da177e4SLinus Torvalds { 1833d7480fd3SWANG Cong neigh_tables[index] = NULL; 18341da177e4SLinus Torvalds /* It is not clean... Fix it to unload IPv6 module safely */ 18354177d5b0SDaniel Borkmann cancel_delayed_work_sync(&tbl->managed_work); 1836a5c30b34STejun Heo cancel_delayed_work_sync(&tbl->gc_work); 18371da177e4SLinus Torvalds del_timer_sync(&tbl->proxy_timer); 1838*8207f253SThomas Zeitlhofer pneigh_queue_purge(&tbl->proxy_queue, NULL, tbl->family); 18391da177e4SLinus Torvalds neigh_ifdown(tbl, NULL); 18401da177e4SLinus Torvalds if (atomic_read(&tbl->entries)) 1841e005d193SJoe Perches pr_crit("neighbour leakage\n"); 18421da177e4SLinus Torvalds 18436193d2beSEric Dumazet call_rcu(&rcu_dereference_protected(tbl->nht, 1)->rcu, 18446193d2beSEric Dumazet neigh_hash_free_rcu); 1845d6bf7817SEric Dumazet tbl->nht = NULL; 18461da177e4SLinus Torvalds 18471da177e4SLinus Torvalds kfree(tbl->phash_buckets); 18481da177e4SLinus Torvalds tbl->phash_buckets = NULL; 18491da177e4SLinus Torvalds 18503f192b5cSAlexey Dobriyan remove_proc_entry(tbl->id, init_net.proc_net_stat); 18513f192b5cSAlexey Dobriyan 18523fcde74bSKirill Korotaev free_percpu(tbl->stats); 18533fcde74bSKirill Korotaev tbl->stats = NULL; 18543fcde74bSKirill Korotaev 18551da177e4SLinus Torvalds return 0; 18561da177e4SLinus Torvalds } 18570a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_table_clear); 18581da177e4SLinus Torvalds 1859d7480fd3SWANG Cong static struct neigh_table *neigh_find_table(int family) 1860d7480fd3SWANG Cong { 1861d7480fd3SWANG Cong struct neigh_table *tbl = NULL; 1862d7480fd3SWANG Cong 1863d7480fd3SWANG Cong switch (family) { 1864d7480fd3SWANG Cong case AF_INET: 1865d7480fd3SWANG Cong tbl = neigh_tables[NEIGH_ARP_TABLE]; 1866d7480fd3SWANG Cong break; 1867d7480fd3SWANG Cong case AF_INET6: 1868d7480fd3SWANG Cong tbl = neigh_tables[NEIGH_ND_TABLE]; 1869d7480fd3SWANG Cong break; 1870d7480fd3SWANG Cong } 1871d7480fd3SWANG Cong 1872d7480fd3SWANG Cong return tbl; 1873d7480fd3SWANG Cong } 1874d7480fd3SWANG Cong 187582cbb5c6SRoopa Prabhu const struct nla_policy nda_policy[NDA_MAX+1] = { 18761274e1ccSRoopa Prabhu [NDA_UNSPEC] = { .strict_start_type = NDA_NH_ID }, 187782cbb5c6SRoopa Prabhu [NDA_DST] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, 187882cbb5c6SRoopa Prabhu [NDA_LLADDR] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, 187982cbb5c6SRoopa Prabhu [NDA_CACHEINFO] = { .len = sizeof(struct nda_cacheinfo) }, 188082cbb5c6SRoopa Prabhu [NDA_PROBES] = { .type = NLA_U32 }, 188182cbb5c6SRoopa Prabhu [NDA_VLAN] = { .type = NLA_U16 }, 188282cbb5c6SRoopa Prabhu [NDA_PORT] = { .type = NLA_U16 }, 188382cbb5c6SRoopa Prabhu [NDA_VNI] = { .type = NLA_U32 }, 188482cbb5c6SRoopa Prabhu [NDA_IFINDEX] = { .type = NLA_U32 }, 188582cbb5c6SRoopa Prabhu [NDA_MASTER] = { .type = NLA_U32 }, 1886a9cd3439SDavid Ahern [NDA_PROTOCOL] = { .type = NLA_U8 }, 18871274e1ccSRoopa Prabhu [NDA_NH_ID] = { .type = NLA_U32 }, 1888c8e80c11SDaniel Borkmann [NDA_FLAGS_EXT] = NLA_POLICY_MASK(NLA_U32, NTF_EXT_MASK), 1889899426b3SNikolay Aleksandrov [NDA_FDB_EXT_ATTRS] = { .type = NLA_NESTED }, 189082cbb5c6SRoopa Prabhu }; 189182cbb5c6SRoopa Prabhu 1892c21ef3e3SDavid Ahern static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, 1893c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 18941da177e4SLinus Torvalds { 18953b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1896a14a49d2SThomas Graf struct ndmsg *ndm; 1897a14a49d2SThomas Graf struct nlattr *dst_attr; 18981da177e4SLinus Torvalds struct neigh_table *tbl; 1899d7480fd3SWANG Cong struct neighbour *neigh; 19001da177e4SLinus Torvalds struct net_device *dev = NULL; 1901a14a49d2SThomas Graf int err = -EINVAL; 19021da177e4SLinus Torvalds 1903110b2499SEric Dumazet ASSERT_RTNL(); 1904a14a49d2SThomas Graf if (nlmsg_len(nlh) < sizeof(*ndm)) 19051da177e4SLinus Torvalds goto out; 19061da177e4SLinus Torvalds 1907a14a49d2SThomas Graf dst_attr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_DST); 19087a35a50dSDavid Ahern if (!dst_attr) { 19097a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Network address not specified"); 1910a14a49d2SThomas Graf goto out; 19117a35a50dSDavid Ahern } 1912a14a49d2SThomas Graf 1913a14a49d2SThomas Graf ndm = nlmsg_data(nlh); 1914a14a49d2SThomas Graf if (ndm->ndm_ifindex) { 1915110b2499SEric Dumazet dev = __dev_get_by_index(net, ndm->ndm_ifindex); 1916a14a49d2SThomas Graf if (dev == NULL) { 1917a14a49d2SThomas Graf err = -ENODEV; 1918a14a49d2SThomas Graf goto out; 1919a14a49d2SThomas Graf } 1920a14a49d2SThomas Graf } 1921a14a49d2SThomas Graf 1922d7480fd3SWANG Cong tbl = neigh_find_table(ndm->ndm_family); 1923d7480fd3SWANG Cong if (tbl == NULL) 1924d7480fd3SWANG Cong return -EAFNOSUPPORT; 19251da177e4SLinus Torvalds 19267a35a50dSDavid Ahern if (nla_len(dst_attr) < (int)tbl->key_len) { 19277a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid network address"); 1928110b2499SEric Dumazet goto out; 19297a35a50dSDavid Ahern } 19301da177e4SLinus Torvalds 19311da177e4SLinus Torvalds if (ndm->ndm_flags & NTF_PROXY) { 1932426b5303SEric W. Biederman err = pneigh_delete(tbl, net, nla_data(dst_attr), dev); 1933110b2499SEric Dumazet goto out; 19341da177e4SLinus Torvalds } 19351da177e4SLinus Torvalds 1936a14a49d2SThomas Graf if (dev == NULL) 1937110b2499SEric Dumazet goto out; 19381da177e4SLinus Torvalds 1939a14a49d2SThomas Graf neigh = neigh_lookup(tbl, nla_data(dst_attr), dev); 1940a14a49d2SThomas Graf if (neigh == NULL) { 1941a14a49d2SThomas Graf err = -ENOENT; 1942110b2499SEric Dumazet goto out; 1943a14a49d2SThomas Graf } 1944a14a49d2SThomas Graf 19457a35a50dSDavid Ahern err = __neigh_update(neigh, NULL, NUD_FAILED, 19467a35a50dSDavid Ahern NEIGH_UPDATE_F_OVERRIDE | NEIGH_UPDATE_F_ADMIN, 19477a35a50dSDavid Ahern NETLINK_CB(skb).portid, extack); 19485071034eSSowmini Varadhan write_lock_bh(&tbl->lock); 1949a14a49d2SThomas Graf neigh_release(neigh); 19505071034eSSowmini Varadhan neigh_remove_one(neigh, tbl); 19515071034eSSowmini Varadhan write_unlock_bh(&tbl->lock); 1952a14a49d2SThomas Graf 19531da177e4SLinus Torvalds out: 19541da177e4SLinus Torvalds return err; 19551da177e4SLinus Torvalds } 19561da177e4SLinus Torvalds 1957c21ef3e3SDavid Ahern static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, 1958c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 19591da177e4SLinus Torvalds { 1960f7aa74e4SRoopa Prabhu int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE | 1961f7aa74e4SRoopa Prabhu NEIGH_UPDATE_F_OVERRIDE_ISROUTER; 19623b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 19635208debdSThomas Graf struct ndmsg *ndm; 19645208debdSThomas Graf struct nlattr *tb[NDA_MAX+1]; 19651da177e4SLinus Torvalds struct neigh_table *tbl; 19661da177e4SLinus Torvalds struct net_device *dev = NULL; 1967d7480fd3SWANG Cong struct neighbour *neigh; 1968d7480fd3SWANG Cong void *dst, *lladdr; 1969df9b0e30SDavid Ahern u8 protocol = 0; 19702c611ad9SRoopa Prabhu u32 ndm_flags; 19715208debdSThomas Graf int err; 19721da177e4SLinus Torvalds 1973110b2499SEric Dumazet ASSERT_RTNL(); 19748cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(*ndm), tb, NDA_MAX, 19758cb08174SJohannes Berg nda_policy, extack); 19765208debdSThomas Graf if (err < 0) 19771da177e4SLinus Torvalds goto out; 19781da177e4SLinus Torvalds 19795208debdSThomas Graf err = -EINVAL; 19807a35a50dSDavid Ahern if (!tb[NDA_DST]) { 19817a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Network address not specified"); 19825208debdSThomas Graf goto out; 19837a35a50dSDavid Ahern } 19845208debdSThomas Graf 19855208debdSThomas Graf ndm = nlmsg_data(nlh); 19862c611ad9SRoopa Prabhu ndm_flags = ndm->ndm_flags; 19872c611ad9SRoopa Prabhu if (tb[NDA_FLAGS_EXT]) { 19882c611ad9SRoopa Prabhu u32 ext = nla_get_u32(tb[NDA_FLAGS_EXT]); 19892c611ad9SRoopa Prabhu 1990507c2f1dSDaniel Borkmann BUILD_BUG_ON(sizeof(neigh->flags) * BITS_PER_BYTE < 1991507c2f1dSDaniel Borkmann (sizeof(ndm->ndm_flags) * BITS_PER_BYTE + 1992507c2f1dSDaniel Borkmann hweight32(NTF_EXT_MASK))); 19932c611ad9SRoopa Prabhu ndm_flags |= (ext << NTF_EXT_SHIFT); 19942c611ad9SRoopa Prabhu } 19955208debdSThomas Graf if (ndm->ndm_ifindex) { 1996110b2499SEric Dumazet dev = __dev_get_by_index(net, ndm->ndm_ifindex); 19975208debdSThomas Graf if (dev == NULL) { 19985208debdSThomas Graf err = -ENODEV; 19995208debdSThomas Graf goto out; 20005208debdSThomas Graf } 20015208debdSThomas Graf 20027a35a50dSDavid Ahern if (tb[NDA_LLADDR] && nla_len(tb[NDA_LLADDR]) < dev->addr_len) { 20037a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid link address"); 2004110b2499SEric Dumazet goto out; 20055208debdSThomas Graf } 20067a35a50dSDavid Ahern } 20075208debdSThomas Graf 2008d7480fd3SWANG Cong tbl = neigh_find_table(ndm->ndm_family); 2009d7480fd3SWANG Cong if (tbl == NULL) 2010d7480fd3SWANG Cong return -EAFNOSUPPORT; 20111da177e4SLinus Torvalds 20127a35a50dSDavid Ahern if (nla_len(tb[NDA_DST]) < (int)tbl->key_len) { 20137a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid network address"); 2014110b2499SEric Dumazet goto out; 20157a35a50dSDavid Ahern } 20167a35a50dSDavid Ahern 20175208debdSThomas Graf dst = nla_data(tb[NDA_DST]); 20185208debdSThomas Graf lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL; 20191da177e4SLinus Torvalds 2020a9cd3439SDavid Ahern if (tb[NDA_PROTOCOL]) 2021df9b0e30SDavid Ahern protocol = nla_get_u8(tb[NDA_PROTOCOL]); 20222c611ad9SRoopa Prabhu if (ndm_flags & NTF_PROXY) { 202362dd9318SVille Nuorvala struct pneigh_entry *pn; 202462dd9318SVille Nuorvala 20257482e384SDaniel Borkmann if (ndm_flags & NTF_MANAGED) { 20267482e384SDaniel Borkmann NL_SET_ERR_MSG(extack, "Invalid NTF_* flag combination"); 20277482e384SDaniel Borkmann goto out; 20287482e384SDaniel Borkmann } 20297482e384SDaniel Borkmann 20305208debdSThomas Graf err = -ENOBUFS; 2031426b5303SEric W. Biederman pn = pneigh_lookup(tbl, net, dst, dev, 1); 203262dd9318SVille Nuorvala if (pn) { 20332c611ad9SRoopa Prabhu pn->flags = ndm_flags; 2034df9b0e30SDavid Ahern if (protocol) 2035df9b0e30SDavid Ahern pn->protocol = protocol; 203662dd9318SVille Nuorvala err = 0; 203762dd9318SVille Nuorvala } 2038110b2499SEric Dumazet goto out; 20391da177e4SLinus Torvalds } 20401da177e4SLinus Torvalds 20417a35a50dSDavid Ahern if (!dev) { 20427a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Device not specified"); 2043110b2499SEric Dumazet goto out; 20447a35a50dSDavid Ahern } 20451da177e4SLinus Torvalds 2046b8fb1ab4SDavid Ahern if (tbl->allow_add && !tbl->allow_add(dev, extack)) { 2047b8fb1ab4SDavid Ahern err = -EINVAL; 2048b8fb1ab4SDavid Ahern goto out; 2049b8fb1ab4SDavid Ahern } 2050b8fb1ab4SDavid Ahern 20515208debdSThomas Graf neigh = neigh_lookup(tbl, dst, dev); 20525208debdSThomas Graf if (neigh == NULL) { 205330fc7efaSDaniel Borkmann bool ndm_permanent = ndm->ndm_state & NUD_PERMANENT; 205430fc7efaSDaniel Borkmann bool exempt_from_gc = ndm_permanent || 205530fc7efaSDaniel Borkmann ndm_flags & NTF_EXT_LEARNED; 2056e997f8a2SDavid Ahern 20575208debdSThomas Graf if (!(nlh->nlmsg_flags & NLM_F_CREATE)) { 20581da177e4SLinus Torvalds err = -ENOENT; 2059110b2499SEric Dumazet goto out; 20605208debdSThomas Graf } 206130fc7efaSDaniel Borkmann if (ndm_permanent && (ndm_flags & NTF_MANAGED)) { 206230fc7efaSDaniel Borkmann NL_SET_ERR_MSG(extack, "Invalid NTF_* flag for permanent entry"); 206330fc7efaSDaniel Borkmann err = -EINVAL; 206430fc7efaSDaniel Borkmann goto out; 206530fc7efaSDaniel Borkmann } 20665208debdSThomas Graf 2067e4400bbfSDaniel Borkmann neigh = ___neigh_create(tbl, dst, dev, 20687482e384SDaniel Borkmann ndm_flags & 20697482e384SDaniel Borkmann (NTF_EXT_LEARNED | NTF_MANAGED), 2070e4400bbfSDaniel Borkmann exempt_from_gc, true); 20715208debdSThomas Graf if (IS_ERR(neigh)) { 20725208debdSThomas Graf err = PTR_ERR(neigh); 2073110b2499SEric Dumazet goto out; 20741da177e4SLinus Torvalds } 20755208debdSThomas Graf } else { 20765208debdSThomas Graf if (nlh->nlmsg_flags & NLM_F_EXCL) { 20775208debdSThomas Graf err = -EEXIST; 20785208debdSThomas Graf neigh_release(neigh); 2079110b2499SEric Dumazet goto out; 20801da177e4SLinus Torvalds } 20811da177e4SLinus Torvalds 20825208debdSThomas Graf if (!(nlh->nlmsg_flags & NLM_F_REPLACE)) 2083f7aa74e4SRoopa Prabhu flags &= ~(NEIGH_UPDATE_F_OVERRIDE | 2084f7aa74e4SRoopa Prabhu NEIGH_UPDATE_F_OVERRIDE_ISROUTER); 20855208debdSThomas Graf } 20861da177e4SLinus Torvalds 208738212bb3SRoman Mashak if (protocol) 208838212bb3SRoman Mashak neigh->protocol = protocol; 20892c611ad9SRoopa Prabhu if (ndm_flags & NTF_EXT_LEARNED) 20909ce33e46SRoopa Prabhu flags |= NEIGH_UPDATE_F_EXT_LEARNED; 20912c611ad9SRoopa Prabhu if (ndm_flags & NTF_ROUTER) 2092f7aa74e4SRoopa Prabhu flags |= NEIGH_UPDATE_F_ISROUTER; 20937482e384SDaniel Borkmann if (ndm_flags & NTF_MANAGED) 20947482e384SDaniel Borkmann flags |= NEIGH_UPDATE_F_MANAGED; 20952c611ad9SRoopa Prabhu if (ndm_flags & NTF_USE) 20963dc20f47SDaniel Borkmann flags |= NEIGH_UPDATE_F_USE; 2097f7aa74e4SRoopa Prabhu 20987a35a50dSDavid Ahern err = __neigh_update(neigh, lladdr, ndm->ndm_state, flags, 20997a35a50dSDavid Ahern NETLINK_CB(skb).portid, extack); 21007482e384SDaniel Borkmann if (!err && ndm_flags & (NTF_USE | NTF_MANAGED)) { 21013dc20f47SDaniel Borkmann neigh_event_send(neigh, NULL); 21023dc20f47SDaniel Borkmann err = 0; 21033dc20f47SDaniel Borkmann } 21045208debdSThomas Graf neigh_release(neigh); 21051da177e4SLinus Torvalds out: 21061da177e4SLinus Torvalds return err; 21071da177e4SLinus Torvalds } 21081da177e4SLinus Torvalds 2109c7fb64dbSThomas Graf static int neightbl_fill_parms(struct sk_buff *skb, struct neigh_parms *parms) 2110c7fb64dbSThomas Graf { 2111ca860fb3SThomas Graf struct nlattr *nest; 2112e386c6ebSThomas Graf 2113ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, NDTA_PARMS); 2114ca860fb3SThomas Graf if (nest == NULL) 2115ca860fb3SThomas Graf return -ENOBUFS; 2116c7fb64dbSThomas Graf 21179a6308d7SDavid S. Miller if ((parms->dev && 21189a6308d7SDavid S. Miller nla_put_u32(skb, NDTPA_IFINDEX, parms->dev->ifindex)) || 21196343944bSReshetova, Elena nla_put_u32(skb, NDTPA_REFCNT, refcount_read(&parms->refcnt)) || 21201f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_QUEUE_LENBYTES, 21211f9248e5SJiri Pirko NEIGH_VAR(parms, QUEUE_LEN_BYTES)) || 21228b5c171bSEric Dumazet /* approximative value for deprecated QUEUE_LEN (in packets) */ 21239a6308d7SDavid S. Miller nla_put_u32(skb, NDTPA_QUEUE_LEN, 21241f9248e5SJiri Pirko NEIGH_VAR(parms, QUEUE_LEN_BYTES) / SKB_TRUESIZE(ETH_FRAME_LEN)) || 21251f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_PROXY_QLEN, NEIGH_VAR(parms, PROXY_QLEN)) || 21261f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_APP_PROBES, NEIGH_VAR(parms, APP_PROBES)) || 21271f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_UCAST_PROBES, 21281f9248e5SJiri Pirko NEIGH_VAR(parms, UCAST_PROBES)) || 21291f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_MCAST_PROBES, 21301f9248e5SJiri Pirko NEIGH_VAR(parms, MCAST_PROBES)) || 21318da86466SYOSHIFUJI Hideaki/吉藤英明 nla_put_u32(skb, NDTPA_MCAST_REPROBES, 21328da86466SYOSHIFUJI Hideaki/吉藤英明 NEIGH_VAR(parms, MCAST_REPROBES)) || 21332175d87cSNicolas Dichtel nla_put_msecs(skb, NDTPA_REACHABLE_TIME, parms->reachable_time, 21342175d87cSNicolas Dichtel NDTPA_PAD) || 21359a6308d7SDavid S. Miller nla_put_msecs(skb, NDTPA_BASE_REACHABLE_TIME, 21362175d87cSNicolas Dichtel NEIGH_VAR(parms, BASE_REACHABLE_TIME), NDTPA_PAD) || 21371f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_GC_STALETIME, 21382175d87cSNicolas Dichtel NEIGH_VAR(parms, GC_STALETIME), NDTPA_PAD) || 21399a6308d7SDavid S. Miller nla_put_msecs(skb, NDTPA_DELAY_PROBE_TIME, 21402175d87cSNicolas Dichtel NEIGH_VAR(parms, DELAY_PROBE_TIME), NDTPA_PAD) || 21411f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_RETRANS_TIME, 21422175d87cSNicolas Dichtel NEIGH_VAR(parms, RETRANS_TIME), NDTPA_PAD) || 21431f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_ANYCAST_DELAY, 21442175d87cSNicolas Dichtel NEIGH_VAR(parms, ANYCAST_DELAY), NDTPA_PAD) || 21451f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_PROXY_DELAY, 21462175d87cSNicolas Dichtel NEIGH_VAR(parms, PROXY_DELAY), NDTPA_PAD) || 21471f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_LOCKTIME, 2148211da42eSYuwei Wang NEIGH_VAR(parms, LOCKTIME), NDTPA_PAD) || 2149211da42eSYuwei Wang nla_put_msecs(skb, NDTPA_INTERVAL_PROBE_TIME_MS, 2150211da42eSYuwei Wang NEIGH_VAR(parms, INTERVAL_PROBE_TIME_MS), NDTPA_PAD)) 21519a6308d7SDavid S. Miller goto nla_put_failure; 2152ca860fb3SThomas Graf return nla_nest_end(skb, nest); 2153c7fb64dbSThomas Graf 2154ca860fb3SThomas Graf nla_put_failure: 2155bc3ed28cSThomas Graf nla_nest_cancel(skb, nest); 2156bc3ed28cSThomas Graf return -EMSGSIZE; 2157c7fb64dbSThomas Graf } 2158c7fb64dbSThomas Graf 2159ca860fb3SThomas Graf static int neightbl_fill_info(struct sk_buff *skb, struct neigh_table *tbl, 2160ca860fb3SThomas Graf u32 pid, u32 seq, int type, int flags) 2161c7fb64dbSThomas Graf { 2162c7fb64dbSThomas Graf struct nlmsghdr *nlh; 2163c7fb64dbSThomas Graf struct ndtmsg *ndtmsg; 2164c7fb64dbSThomas Graf 2165ca860fb3SThomas Graf nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndtmsg), flags); 2166ca860fb3SThomas Graf if (nlh == NULL) 216726932566SPatrick McHardy return -EMSGSIZE; 2168c7fb64dbSThomas Graf 2169ca860fb3SThomas Graf ndtmsg = nlmsg_data(nlh); 2170c7fb64dbSThomas Graf 2171c7fb64dbSThomas Graf read_lock_bh(&tbl->lock); 2172c7fb64dbSThomas Graf ndtmsg->ndtm_family = tbl->family; 21739ef1d4c7SPatrick McHardy ndtmsg->ndtm_pad1 = 0; 21749ef1d4c7SPatrick McHardy ndtmsg->ndtm_pad2 = 0; 2175c7fb64dbSThomas Graf 21769a6308d7SDavid S. Miller if (nla_put_string(skb, NDTA_NAME, tbl->id) || 21772175d87cSNicolas Dichtel nla_put_msecs(skb, NDTA_GC_INTERVAL, tbl->gc_interval, NDTA_PAD) || 21789a6308d7SDavid S. Miller nla_put_u32(skb, NDTA_THRESH1, tbl->gc_thresh1) || 21799a6308d7SDavid S. Miller nla_put_u32(skb, NDTA_THRESH2, tbl->gc_thresh2) || 21809a6308d7SDavid S. Miller nla_put_u32(skb, NDTA_THRESH3, tbl->gc_thresh3)) 21819a6308d7SDavid S. Miller goto nla_put_failure; 2182c7fb64dbSThomas Graf { 2183c7fb64dbSThomas Graf unsigned long now = jiffies; 21849d027e3aSEric Dumazet long flush_delta = now - tbl->last_flush; 21859d027e3aSEric Dumazet long rand_delta = now - tbl->last_rand; 2186d6bf7817SEric Dumazet struct neigh_hash_table *nht; 2187c7fb64dbSThomas Graf struct ndt_config ndc = { 2188c7fb64dbSThomas Graf .ndtc_key_len = tbl->key_len, 2189c7fb64dbSThomas Graf .ndtc_entry_size = tbl->entry_size, 2190c7fb64dbSThomas Graf .ndtc_entries = atomic_read(&tbl->entries), 2191c7fb64dbSThomas Graf .ndtc_last_flush = jiffies_to_msecs(flush_delta), 2192c7fb64dbSThomas Graf .ndtc_last_rand = jiffies_to_msecs(rand_delta), 2193c7fb64dbSThomas Graf .ndtc_proxy_qlen = tbl->proxy_queue.qlen, 2194c7fb64dbSThomas Graf }; 2195c7fb64dbSThomas Graf 2196d6bf7817SEric Dumazet rcu_read_lock_bh(); 2197d6bf7817SEric Dumazet nht = rcu_dereference_bh(tbl->nht); 21982c2aba6cSDavid S. Miller ndc.ndtc_hash_rnd = nht->hash_rnd[0]; 2199cd089336SDavid S. Miller ndc.ndtc_hash_mask = ((1 << nht->hash_shift) - 1); 2200d6bf7817SEric Dumazet rcu_read_unlock_bh(); 2201d6bf7817SEric Dumazet 22029a6308d7SDavid S. Miller if (nla_put(skb, NDTA_CONFIG, sizeof(ndc), &ndc)) 22039a6308d7SDavid S. Miller goto nla_put_failure; 2204c7fb64dbSThomas Graf } 2205c7fb64dbSThomas Graf 2206c7fb64dbSThomas Graf { 2207c7fb64dbSThomas Graf int cpu; 2208c7fb64dbSThomas Graf struct ndt_stats ndst; 2209c7fb64dbSThomas Graf 2210c7fb64dbSThomas Graf memset(&ndst, 0, sizeof(ndst)); 2211c7fb64dbSThomas Graf 22126f912042SKAMEZAWA Hiroyuki for_each_possible_cpu(cpu) { 2213c7fb64dbSThomas Graf struct neigh_statistics *st; 2214c7fb64dbSThomas Graf 2215c7fb64dbSThomas Graf st = per_cpu_ptr(tbl->stats, cpu); 2216c7fb64dbSThomas Graf ndst.ndts_allocs += st->allocs; 2217c7fb64dbSThomas Graf ndst.ndts_destroys += st->destroys; 2218c7fb64dbSThomas Graf ndst.ndts_hash_grows += st->hash_grows; 2219c7fb64dbSThomas Graf ndst.ndts_res_failed += st->res_failed; 2220c7fb64dbSThomas Graf ndst.ndts_lookups += st->lookups; 2221c7fb64dbSThomas Graf ndst.ndts_hits += st->hits; 2222c7fb64dbSThomas Graf ndst.ndts_rcv_probes_mcast += st->rcv_probes_mcast; 2223c7fb64dbSThomas Graf ndst.ndts_rcv_probes_ucast += st->rcv_probes_ucast; 2224c7fb64dbSThomas Graf ndst.ndts_periodic_gc_runs += st->periodic_gc_runs; 2225c7fb64dbSThomas Graf ndst.ndts_forced_gc_runs += st->forced_gc_runs; 2226fb811395SRick Jones ndst.ndts_table_fulls += st->table_fulls; 2227c7fb64dbSThomas Graf } 2228c7fb64dbSThomas Graf 2229b676338fSNicolas Dichtel if (nla_put_64bit(skb, NDTA_STATS, sizeof(ndst), &ndst, 2230b676338fSNicolas Dichtel NDTA_PAD)) 22319a6308d7SDavid S. Miller goto nla_put_failure; 2232c7fb64dbSThomas Graf } 2233c7fb64dbSThomas Graf 2234c7fb64dbSThomas Graf BUG_ON(tbl->parms.dev); 2235c7fb64dbSThomas Graf if (neightbl_fill_parms(skb, &tbl->parms) < 0) 2236ca860fb3SThomas Graf goto nla_put_failure; 2237c7fb64dbSThomas Graf 2238c7fb64dbSThomas Graf read_unlock_bh(&tbl->lock); 2239053c095aSJohannes Berg nlmsg_end(skb, nlh); 2240053c095aSJohannes Berg return 0; 2241c7fb64dbSThomas Graf 2242ca860fb3SThomas Graf nla_put_failure: 2243c7fb64dbSThomas Graf read_unlock_bh(&tbl->lock); 224426932566SPatrick McHardy nlmsg_cancel(skb, nlh); 224526932566SPatrick McHardy return -EMSGSIZE; 2246c7fb64dbSThomas Graf } 2247c7fb64dbSThomas Graf 2248ca860fb3SThomas Graf static int neightbl_fill_param_info(struct sk_buff *skb, 2249ca860fb3SThomas Graf struct neigh_table *tbl, 2250c7fb64dbSThomas Graf struct neigh_parms *parms, 2251ca860fb3SThomas Graf u32 pid, u32 seq, int type, 2252ca860fb3SThomas Graf unsigned int flags) 2253c7fb64dbSThomas Graf { 2254c7fb64dbSThomas Graf struct ndtmsg *ndtmsg; 2255c7fb64dbSThomas Graf struct nlmsghdr *nlh; 2256c7fb64dbSThomas Graf 2257ca860fb3SThomas Graf nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndtmsg), flags); 2258ca860fb3SThomas Graf if (nlh == NULL) 225926932566SPatrick McHardy return -EMSGSIZE; 2260c7fb64dbSThomas Graf 2261ca860fb3SThomas Graf ndtmsg = nlmsg_data(nlh); 2262c7fb64dbSThomas Graf 2263c7fb64dbSThomas Graf read_lock_bh(&tbl->lock); 2264c7fb64dbSThomas Graf ndtmsg->ndtm_family = tbl->family; 22659ef1d4c7SPatrick McHardy ndtmsg->ndtm_pad1 = 0; 22669ef1d4c7SPatrick McHardy ndtmsg->ndtm_pad2 = 0; 2267c7fb64dbSThomas Graf 2268ca860fb3SThomas Graf if (nla_put_string(skb, NDTA_NAME, tbl->id) < 0 || 2269ca860fb3SThomas Graf neightbl_fill_parms(skb, parms) < 0) 2270ca860fb3SThomas Graf goto errout; 2271c7fb64dbSThomas Graf 2272c7fb64dbSThomas Graf read_unlock_bh(&tbl->lock); 2273053c095aSJohannes Berg nlmsg_end(skb, nlh); 2274053c095aSJohannes Berg return 0; 2275ca860fb3SThomas Graf errout: 2276c7fb64dbSThomas Graf read_unlock_bh(&tbl->lock); 227726932566SPatrick McHardy nlmsg_cancel(skb, nlh); 227826932566SPatrick McHardy return -EMSGSIZE; 2279c7fb64dbSThomas Graf } 2280c7fb64dbSThomas Graf 2281ef7c79edSPatrick McHardy static const struct nla_policy nl_neightbl_policy[NDTA_MAX+1] = { 22826b3f8674SThomas Graf [NDTA_NAME] = { .type = NLA_STRING }, 22836b3f8674SThomas Graf [NDTA_THRESH1] = { .type = NLA_U32 }, 22846b3f8674SThomas Graf [NDTA_THRESH2] = { .type = NLA_U32 }, 22856b3f8674SThomas Graf [NDTA_THRESH3] = { .type = NLA_U32 }, 22866b3f8674SThomas Graf [NDTA_GC_INTERVAL] = { .type = NLA_U64 }, 22876b3f8674SThomas Graf [NDTA_PARMS] = { .type = NLA_NESTED }, 22886b3f8674SThomas Graf }; 22896b3f8674SThomas Graf 2290ef7c79edSPatrick McHardy static const struct nla_policy nl_ntbl_parm_policy[NDTPA_MAX+1] = { 22916b3f8674SThomas Graf [NDTPA_IFINDEX] = { .type = NLA_U32 }, 22926b3f8674SThomas Graf [NDTPA_QUEUE_LEN] = { .type = NLA_U32 }, 22936b3f8674SThomas Graf [NDTPA_PROXY_QLEN] = { .type = NLA_U32 }, 22946b3f8674SThomas Graf [NDTPA_APP_PROBES] = { .type = NLA_U32 }, 22956b3f8674SThomas Graf [NDTPA_UCAST_PROBES] = { .type = NLA_U32 }, 22966b3f8674SThomas Graf [NDTPA_MCAST_PROBES] = { .type = NLA_U32 }, 22978da86466SYOSHIFUJI Hideaki/吉藤英明 [NDTPA_MCAST_REPROBES] = { .type = NLA_U32 }, 22986b3f8674SThomas Graf [NDTPA_BASE_REACHABLE_TIME] = { .type = NLA_U64 }, 22996b3f8674SThomas Graf [NDTPA_GC_STALETIME] = { .type = NLA_U64 }, 23006b3f8674SThomas Graf [NDTPA_DELAY_PROBE_TIME] = { .type = NLA_U64 }, 23016b3f8674SThomas Graf [NDTPA_RETRANS_TIME] = { .type = NLA_U64 }, 23026b3f8674SThomas Graf [NDTPA_ANYCAST_DELAY] = { .type = NLA_U64 }, 23036b3f8674SThomas Graf [NDTPA_PROXY_DELAY] = { .type = NLA_U64 }, 23046b3f8674SThomas Graf [NDTPA_LOCKTIME] = { .type = NLA_U64 }, 2305211da42eSYuwei Wang [NDTPA_INTERVAL_PROBE_TIME_MS] = { .type = NLA_U64, .min = 1 }, 23066b3f8674SThomas Graf }; 23076b3f8674SThomas Graf 2308c21ef3e3SDavid Ahern static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, 2309c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 2310c7fb64dbSThomas Graf { 23113b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 2312c7fb64dbSThomas Graf struct neigh_table *tbl; 23136b3f8674SThomas Graf struct ndtmsg *ndtmsg; 23146b3f8674SThomas Graf struct nlattr *tb[NDTA_MAX+1]; 2315d7480fd3SWANG Cong bool found = false; 2316d7480fd3SWANG Cong int err, tidx; 2317c7fb64dbSThomas Graf 23188cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(*ndtmsg), tb, NDTA_MAX, 2319c21ef3e3SDavid Ahern nl_neightbl_policy, extack); 23206b3f8674SThomas Graf if (err < 0) 23216b3f8674SThomas Graf goto errout; 2322c7fb64dbSThomas Graf 23236b3f8674SThomas Graf if (tb[NDTA_NAME] == NULL) { 23246b3f8674SThomas Graf err = -EINVAL; 23256b3f8674SThomas Graf goto errout; 23266b3f8674SThomas Graf } 23276b3f8674SThomas Graf 23286b3f8674SThomas Graf ndtmsg = nlmsg_data(nlh); 2329d7480fd3SWANG Cong 2330d7480fd3SWANG Cong for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) { 2331d7480fd3SWANG Cong tbl = neigh_tables[tidx]; 2332d7480fd3SWANG Cong if (!tbl) 2333d7480fd3SWANG Cong continue; 2334c7fb64dbSThomas Graf if (ndtmsg->ndtm_family && tbl->family != ndtmsg->ndtm_family) 2335c7fb64dbSThomas Graf continue; 2336d7480fd3SWANG Cong if (nla_strcmp(tb[NDTA_NAME], tbl->id) == 0) { 2337d7480fd3SWANG Cong found = true; 2338c7fb64dbSThomas Graf break; 2339c7fb64dbSThomas Graf } 2340c7fb64dbSThomas Graf } 2341c7fb64dbSThomas Graf 2342d7480fd3SWANG Cong if (!found) 2343d7480fd3SWANG Cong return -ENOENT; 2344d7480fd3SWANG Cong 2345c7fb64dbSThomas Graf /* 2346c7fb64dbSThomas Graf * We acquire tbl->lock to be nice to the periodic timers and 2347c7fb64dbSThomas Graf * make sure they always see a consistent set of values. 2348c7fb64dbSThomas Graf */ 2349c7fb64dbSThomas Graf write_lock_bh(&tbl->lock); 2350c7fb64dbSThomas Graf 23516b3f8674SThomas Graf if (tb[NDTA_PARMS]) { 23526b3f8674SThomas Graf struct nlattr *tbp[NDTPA_MAX+1]; 2353c7fb64dbSThomas Graf struct neigh_parms *p; 23546b3f8674SThomas Graf int i, ifindex = 0; 2355c7fb64dbSThomas Graf 23568cb08174SJohannes Berg err = nla_parse_nested_deprecated(tbp, NDTPA_MAX, 23578cb08174SJohannes Berg tb[NDTA_PARMS], 2358c21ef3e3SDavid Ahern nl_ntbl_parm_policy, extack); 23596b3f8674SThomas Graf if (err < 0) 23606b3f8674SThomas Graf goto errout_tbl_lock; 2361c7fb64dbSThomas Graf 23626b3f8674SThomas Graf if (tbp[NDTPA_IFINDEX]) 23636b3f8674SThomas Graf ifindex = nla_get_u32(tbp[NDTPA_IFINDEX]); 2364c7fb64dbSThomas Graf 236597fd5bc7STobias Klauser p = lookup_neigh_parms(tbl, net, ifindex); 2366c7fb64dbSThomas Graf if (p == NULL) { 2367c7fb64dbSThomas Graf err = -ENOENT; 23686b3f8674SThomas Graf goto errout_tbl_lock; 2369c7fb64dbSThomas Graf } 2370c7fb64dbSThomas Graf 23716b3f8674SThomas Graf for (i = 1; i <= NDTPA_MAX; i++) { 23726b3f8674SThomas Graf if (tbp[i] == NULL) 23736b3f8674SThomas Graf continue; 2374c7fb64dbSThomas Graf 23756b3f8674SThomas Graf switch (i) { 23766b3f8674SThomas Graf case NDTPA_QUEUE_LEN: 23771f9248e5SJiri Pirko NEIGH_VAR_SET(p, QUEUE_LEN_BYTES, 23781f9248e5SJiri Pirko nla_get_u32(tbp[i]) * 23791f9248e5SJiri Pirko SKB_TRUESIZE(ETH_FRAME_LEN)); 23808b5c171bSEric Dumazet break; 23818b5c171bSEric Dumazet case NDTPA_QUEUE_LENBYTES: 23821f9248e5SJiri Pirko NEIGH_VAR_SET(p, QUEUE_LEN_BYTES, 23831f9248e5SJiri Pirko nla_get_u32(tbp[i])); 23846b3f8674SThomas Graf break; 23856b3f8674SThomas Graf case NDTPA_PROXY_QLEN: 23861f9248e5SJiri Pirko NEIGH_VAR_SET(p, PROXY_QLEN, 23871f9248e5SJiri Pirko nla_get_u32(tbp[i])); 23886b3f8674SThomas Graf break; 23896b3f8674SThomas Graf case NDTPA_APP_PROBES: 23901f9248e5SJiri Pirko NEIGH_VAR_SET(p, APP_PROBES, 23911f9248e5SJiri Pirko nla_get_u32(tbp[i])); 23926b3f8674SThomas Graf break; 23936b3f8674SThomas Graf case NDTPA_UCAST_PROBES: 23941f9248e5SJiri Pirko NEIGH_VAR_SET(p, UCAST_PROBES, 23951f9248e5SJiri Pirko nla_get_u32(tbp[i])); 23966b3f8674SThomas Graf break; 23976b3f8674SThomas Graf case NDTPA_MCAST_PROBES: 23981f9248e5SJiri Pirko NEIGH_VAR_SET(p, MCAST_PROBES, 23991f9248e5SJiri Pirko nla_get_u32(tbp[i])); 24006b3f8674SThomas Graf break; 24018da86466SYOSHIFUJI Hideaki/吉藤英明 case NDTPA_MCAST_REPROBES: 24028da86466SYOSHIFUJI Hideaki/吉藤英明 NEIGH_VAR_SET(p, MCAST_REPROBES, 24038da86466SYOSHIFUJI Hideaki/吉藤英明 nla_get_u32(tbp[i])); 24048da86466SYOSHIFUJI Hideaki/吉藤英明 break; 24056b3f8674SThomas Graf case NDTPA_BASE_REACHABLE_TIME: 24061f9248e5SJiri Pirko NEIGH_VAR_SET(p, BASE_REACHABLE_TIME, 24071f9248e5SJiri Pirko nla_get_msecs(tbp[i])); 24084bf6980dSJean-Francois Remy /* update reachable_time as well, otherwise, the change will 24094bf6980dSJean-Francois Remy * only be effective after the next time neigh_periodic_work 24104bf6980dSJean-Francois Remy * decides to recompute it (can be multiple minutes) 24114bf6980dSJean-Francois Remy */ 24124bf6980dSJean-Francois Remy p->reachable_time = 24134bf6980dSJean-Francois Remy neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); 24146b3f8674SThomas Graf break; 24156b3f8674SThomas Graf case NDTPA_GC_STALETIME: 24161f9248e5SJiri Pirko NEIGH_VAR_SET(p, GC_STALETIME, 24171f9248e5SJiri Pirko nla_get_msecs(tbp[i])); 24186b3f8674SThomas Graf break; 24196b3f8674SThomas Graf case NDTPA_DELAY_PROBE_TIME: 24201f9248e5SJiri Pirko NEIGH_VAR_SET(p, DELAY_PROBE_TIME, 24211f9248e5SJiri Pirko nla_get_msecs(tbp[i])); 24222a4501aeSIdo Schimmel call_netevent_notifiers(NETEVENT_DELAY_PROBE_TIME_UPDATE, p); 24236b3f8674SThomas Graf break; 2424211da42eSYuwei Wang case NDTPA_INTERVAL_PROBE_TIME_MS: 2425211da42eSYuwei Wang NEIGH_VAR_SET(p, INTERVAL_PROBE_TIME_MS, 2426211da42eSYuwei Wang nla_get_msecs(tbp[i])); 2427211da42eSYuwei Wang break; 24286b3f8674SThomas Graf case NDTPA_RETRANS_TIME: 24291f9248e5SJiri Pirko NEIGH_VAR_SET(p, RETRANS_TIME, 24301f9248e5SJiri Pirko nla_get_msecs(tbp[i])); 24316b3f8674SThomas Graf break; 24326b3f8674SThomas Graf case NDTPA_ANYCAST_DELAY: 24333977458cSJiri Pirko NEIGH_VAR_SET(p, ANYCAST_DELAY, 24343977458cSJiri Pirko nla_get_msecs(tbp[i])); 24356b3f8674SThomas Graf break; 24366b3f8674SThomas Graf case NDTPA_PROXY_DELAY: 24373977458cSJiri Pirko NEIGH_VAR_SET(p, PROXY_DELAY, 24383977458cSJiri Pirko nla_get_msecs(tbp[i])); 24396b3f8674SThomas Graf break; 24406b3f8674SThomas Graf case NDTPA_LOCKTIME: 24413977458cSJiri Pirko NEIGH_VAR_SET(p, LOCKTIME, 24423977458cSJiri Pirko nla_get_msecs(tbp[i])); 24436b3f8674SThomas Graf break; 2444c7fb64dbSThomas Graf } 24456b3f8674SThomas Graf } 24466b3f8674SThomas Graf } 24476b3f8674SThomas Graf 2448dc25c676SGao feng err = -ENOENT; 2449dc25c676SGao feng if ((tb[NDTA_THRESH1] || tb[NDTA_THRESH2] || 2450dc25c676SGao feng tb[NDTA_THRESH3] || tb[NDTA_GC_INTERVAL]) && 2451dc25c676SGao feng !net_eq(net, &init_net)) 2452dc25c676SGao feng goto errout_tbl_lock; 2453dc25c676SGao feng 24546b3f8674SThomas Graf if (tb[NDTA_THRESH1]) 24556b3f8674SThomas Graf tbl->gc_thresh1 = nla_get_u32(tb[NDTA_THRESH1]); 24566b3f8674SThomas Graf 24576b3f8674SThomas Graf if (tb[NDTA_THRESH2]) 24586b3f8674SThomas Graf tbl->gc_thresh2 = nla_get_u32(tb[NDTA_THRESH2]); 24596b3f8674SThomas Graf 24606b3f8674SThomas Graf if (tb[NDTA_THRESH3]) 24616b3f8674SThomas Graf tbl->gc_thresh3 = nla_get_u32(tb[NDTA_THRESH3]); 24626b3f8674SThomas Graf 24636b3f8674SThomas Graf if (tb[NDTA_GC_INTERVAL]) 24646b3f8674SThomas Graf tbl->gc_interval = nla_get_msecs(tb[NDTA_GC_INTERVAL]); 2465c7fb64dbSThomas Graf 2466c7fb64dbSThomas Graf err = 0; 2467c7fb64dbSThomas Graf 24686b3f8674SThomas Graf errout_tbl_lock: 2469c7fb64dbSThomas Graf write_unlock_bh(&tbl->lock); 24706b3f8674SThomas Graf errout: 2471c7fb64dbSThomas Graf return err; 2472c7fb64dbSThomas Graf } 2473c7fb64dbSThomas Graf 24749632d47fSDavid Ahern static int neightbl_valid_dump_info(const struct nlmsghdr *nlh, 24759632d47fSDavid Ahern struct netlink_ext_ack *extack) 24769632d47fSDavid Ahern { 24779632d47fSDavid Ahern struct ndtmsg *ndtm; 24789632d47fSDavid Ahern 24799632d47fSDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndtm))) { 24809632d47fSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid header for neighbor table dump request"); 24819632d47fSDavid Ahern return -EINVAL; 24829632d47fSDavid Ahern } 24839632d47fSDavid Ahern 24849632d47fSDavid Ahern ndtm = nlmsg_data(nlh); 24859632d47fSDavid Ahern if (ndtm->ndtm_pad1 || ndtm->ndtm_pad2) { 24869632d47fSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor table dump request"); 24879632d47fSDavid Ahern return -EINVAL; 24889632d47fSDavid Ahern } 24899632d47fSDavid Ahern 24909632d47fSDavid Ahern if (nlmsg_attrlen(nlh, sizeof(*ndtm))) { 24919632d47fSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid data after header in neighbor table dump request"); 24929632d47fSDavid Ahern return -EINVAL; 24939632d47fSDavid Ahern } 24949632d47fSDavid Ahern 24959632d47fSDavid Ahern return 0; 24969632d47fSDavid Ahern } 24979632d47fSDavid Ahern 2498c8822a4eSThomas Graf static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb) 2499c7fb64dbSThomas Graf { 25009632d47fSDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 25013b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 2502ca860fb3SThomas Graf int family, tidx, nidx = 0; 2503ca860fb3SThomas Graf int tbl_skip = cb->args[0]; 2504ca860fb3SThomas Graf int neigh_skip = cb->args[1]; 2505c7fb64dbSThomas Graf struct neigh_table *tbl; 2506c7fb64dbSThomas Graf 25079632d47fSDavid Ahern if (cb->strict_check) { 25089632d47fSDavid Ahern int err = neightbl_valid_dump_info(nlh, cb->extack); 25099632d47fSDavid Ahern 25109632d47fSDavid Ahern if (err < 0) 25119632d47fSDavid Ahern return err; 25129632d47fSDavid Ahern } 25139632d47fSDavid Ahern 25149632d47fSDavid Ahern family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family; 2515c7fb64dbSThomas Graf 2516d7480fd3SWANG Cong for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) { 2517c7fb64dbSThomas Graf struct neigh_parms *p; 2518c7fb64dbSThomas Graf 2519d7480fd3SWANG Cong tbl = neigh_tables[tidx]; 2520d7480fd3SWANG Cong if (!tbl) 2521d7480fd3SWANG Cong continue; 2522d7480fd3SWANG Cong 2523ca860fb3SThomas Graf if (tidx < tbl_skip || (family && tbl->family != family)) 2524c7fb64dbSThomas Graf continue; 2525c7fb64dbSThomas Graf 252615e47304SEric W. Biederman if (neightbl_fill_info(skb, tbl, NETLINK_CB(cb->skb).portid, 25279632d47fSDavid Ahern nlh->nlmsg_seq, RTM_NEWNEIGHTBL, 25287b46a644SDavid S. Miller NLM_F_MULTI) < 0) 2529c7fb64dbSThomas Graf break; 2530c7fb64dbSThomas Graf 253175fbfd33SNicolas Dichtel nidx = 0; 253275fbfd33SNicolas Dichtel p = list_next_entry(&tbl->parms, list); 253375fbfd33SNicolas Dichtel list_for_each_entry_from(p, &tbl->parms_list, list) { 2534878628fbSYOSHIFUJI Hideaki if (!net_eq(neigh_parms_net(p), net)) 2535426b5303SEric W. Biederman continue; 2536426b5303SEric W. Biederman 2537efc683fcSGautam Kachroo if (nidx < neigh_skip) 2538efc683fcSGautam Kachroo goto next; 2539c7fb64dbSThomas Graf 2540ca860fb3SThomas Graf if (neightbl_fill_param_info(skb, tbl, p, 254115e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 25429632d47fSDavid Ahern nlh->nlmsg_seq, 2543ca860fb3SThomas Graf RTM_NEWNEIGHTBL, 25447b46a644SDavid S. Miller NLM_F_MULTI) < 0) 2545c7fb64dbSThomas Graf goto out; 2546efc683fcSGautam Kachroo next: 2547efc683fcSGautam Kachroo nidx++; 2548c7fb64dbSThomas Graf } 2549c7fb64dbSThomas Graf 2550ca860fb3SThomas Graf neigh_skip = 0; 2551c7fb64dbSThomas Graf } 2552c7fb64dbSThomas Graf out: 2553ca860fb3SThomas Graf cb->args[0] = tidx; 2554ca860fb3SThomas Graf cb->args[1] = nidx; 2555c7fb64dbSThomas Graf 2556c7fb64dbSThomas Graf return skb->len; 2557c7fb64dbSThomas Graf } 25581da177e4SLinus Torvalds 25598b8aec50SThomas Graf static int neigh_fill_info(struct sk_buff *skb, struct neighbour *neigh, 25608b8aec50SThomas Graf u32 pid, u32 seq, int type, unsigned int flags) 25611da177e4SLinus Torvalds { 25622c611ad9SRoopa Prabhu u32 neigh_flags, neigh_flags_ext; 25631da177e4SLinus Torvalds unsigned long now = jiffies; 25641da177e4SLinus Torvalds struct nda_cacheinfo ci; 25658b8aec50SThomas Graf struct nlmsghdr *nlh; 25668b8aec50SThomas Graf struct ndmsg *ndm; 25671da177e4SLinus Torvalds 25688b8aec50SThomas Graf nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags); 25698b8aec50SThomas Graf if (nlh == NULL) 257026932566SPatrick McHardy return -EMSGSIZE; 25718b8aec50SThomas Graf 25722c611ad9SRoopa Prabhu neigh_flags_ext = neigh->flags >> NTF_EXT_SHIFT; 25732c611ad9SRoopa Prabhu neigh_flags = neigh->flags & NTF_OLD_MASK; 25742c611ad9SRoopa Prabhu 25758b8aec50SThomas Graf ndm = nlmsg_data(nlh); 25768b8aec50SThomas Graf ndm->ndm_family = neigh->ops->family; 25779ef1d4c7SPatrick McHardy ndm->ndm_pad1 = 0; 25789ef1d4c7SPatrick McHardy ndm->ndm_pad2 = 0; 25792c611ad9SRoopa Prabhu ndm->ndm_flags = neigh_flags; 25808b8aec50SThomas Graf ndm->ndm_type = neigh->type; 25818b8aec50SThomas Graf ndm->ndm_ifindex = neigh->dev->ifindex; 25821da177e4SLinus Torvalds 25839a6308d7SDavid S. Miller if (nla_put(skb, NDA_DST, neigh->tbl->key_len, neigh->primary_key)) 25849a6308d7SDavid S. Miller goto nla_put_failure; 25858b8aec50SThomas Graf 25868b8aec50SThomas Graf read_lock_bh(&neigh->lock); 25878b8aec50SThomas Graf ndm->ndm_state = neigh->nud_state; 25880ed8ddf4SEric Dumazet if (neigh->nud_state & NUD_VALID) { 25890ed8ddf4SEric Dumazet char haddr[MAX_ADDR_LEN]; 25900ed8ddf4SEric Dumazet 25910ed8ddf4SEric Dumazet neigh_ha_snapshot(haddr, neigh, neigh->dev); 25920ed8ddf4SEric Dumazet if (nla_put(skb, NDA_LLADDR, neigh->dev->addr_len, haddr) < 0) { 25938b8aec50SThomas Graf read_unlock_bh(&neigh->lock); 25948b8aec50SThomas Graf goto nla_put_failure; 25958b8aec50SThomas Graf } 25960ed8ddf4SEric Dumazet } 25978b8aec50SThomas Graf 2598b9f5f52cSStephen Hemminger ci.ndm_used = jiffies_to_clock_t(now - neigh->used); 2599b9f5f52cSStephen Hemminger ci.ndm_confirmed = jiffies_to_clock_t(now - neigh->confirmed); 2600b9f5f52cSStephen Hemminger ci.ndm_updated = jiffies_to_clock_t(now - neigh->updated); 26019f237430SReshetova, Elena ci.ndm_refcnt = refcount_read(&neigh->refcnt) - 1; 26028b8aec50SThomas Graf read_unlock_bh(&neigh->lock); 26038b8aec50SThomas Graf 26049a6308d7SDavid S. Miller if (nla_put_u32(skb, NDA_PROBES, atomic_read(&neigh->probes)) || 26059a6308d7SDavid S. Miller nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci)) 26069a6308d7SDavid S. Miller goto nla_put_failure; 26078b8aec50SThomas Graf 2608df9b0e30SDavid Ahern if (neigh->protocol && nla_put_u8(skb, NDA_PROTOCOL, neigh->protocol)) 2609df9b0e30SDavid Ahern goto nla_put_failure; 26102c611ad9SRoopa Prabhu if (neigh_flags_ext && nla_put_u32(skb, NDA_FLAGS_EXT, neigh_flags_ext)) 26112c611ad9SRoopa Prabhu goto nla_put_failure; 2612df9b0e30SDavid Ahern 2613053c095aSJohannes Berg nlmsg_end(skb, nlh); 2614053c095aSJohannes Berg return 0; 26158b8aec50SThomas Graf 26168b8aec50SThomas Graf nla_put_failure: 261726932566SPatrick McHardy nlmsg_cancel(skb, nlh); 261826932566SPatrick McHardy return -EMSGSIZE; 26191da177e4SLinus Torvalds } 26201da177e4SLinus Torvalds 262184920c14STony Zelenoff static int pneigh_fill_info(struct sk_buff *skb, struct pneigh_entry *pn, 262284920c14STony Zelenoff u32 pid, u32 seq, int type, unsigned int flags, 262384920c14STony Zelenoff struct neigh_table *tbl) 262484920c14STony Zelenoff { 26252c611ad9SRoopa Prabhu u32 neigh_flags, neigh_flags_ext; 262684920c14STony Zelenoff struct nlmsghdr *nlh; 262784920c14STony Zelenoff struct ndmsg *ndm; 262884920c14STony Zelenoff 262984920c14STony Zelenoff nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags); 263084920c14STony Zelenoff if (nlh == NULL) 263184920c14STony Zelenoff return -EMSGSIZE; 263284920c14STony Zelenoff 26332c611ad9SRoopa Prabhu neigh_flags_ext = pn->flags >> NTF_EXT_SHIFT; 26342c611ad9SRoopa Prabhu neigh_flags = pn->flags & NTF_OLD_MASK; 26352c611ad9SRoopa Prabhu 263684920c14STony Zelenoff ndm = nlmsg_data(nlh); 263784920c14STony Zelenoff ndm->ndm_family = tbl->family; 263884920c14STony Zelenoff ndm->ndm_pad1 = 0; 263984920c14STony Zelenoff ndm->ndm_pad2 = 0; 26402c611ad9SRoopa Prabhu ndm->ndm_flags = neigh_flags | NTF_PROXY; 2641545469f7SJun Zhao ndm->ndm_type = RTN_UNICAST; 26426adc5fd6SKonstantin Khlebnikov ndm->ndm_ifindex = pn->dev ? pn->dev->ifindex : 0; 264384920c14STony Zelenoff ndm->ndm_state = NUD_NONE; 264484920c14STony Zelenoff 26459a6308d7SDavid S. Miller if (nla_put(skb, NDA_DST, tbl->key_len, pn->key)) 26469a6308d7SDavid S. Miller goto nla_put_failure; 264784920c14STony Zelenoff 2648df9b0e30SDavid Ahern if (pn->protocol && nla_put_u8(skb, NDA_PROTOCOL, pn->protocol)) 2649df9b0e30SDavid Ahern goto nla_put_failure; 26502c611ad9SRoopa Prabhu if (neigh_flags_ext && nla_put_u32(skb, NDA_FLAGS_EXT, neigh_flags_ext)) 26512c611ad9SRoopa Prabhu goto nla_put_failure; 2652df9b0e30SDavid Ahern 2653053c095aSJohannes Berg nlmsg_end(skb, nlh); 2654053c095aSJohannes Berg return 0; 265584920c14STony Zelenoff 265684920c14STony Zelenoff nla_put_failure: 265784920c14STony Zelenoff nlmsg_cancel(skb, nlh); 265884920c14STony Zelenoff return -EMSGSIZE; 265984920c14STony Zelenoff } 266084920c14STony Zelenoff 26617b8f7a40SRoopa Prabhu static void neigh_update_notify(struct neighbour *neigh, u32 nlmsg_pid) 2662d961db35SThomas Graf { 2663d961db35SThomas Graf call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh); 26647b8f7a40SRoopa Prabhu __neigh_notify(neigh, RTM_NEWNEIGH, 0, nlmsg_pid); 2665d961db35SThomas Graf } 26661da177e4SLinus Torvalds 266721fdd092SDavid Ahern static bool neigh_master_filtered(struct net_device *dev, int master_idx) 266821fdd092SDavid Ahern { 266921fdd092SDavid Ahern struct net_device *master; 267021fdd092SDavid Ahern 267121fdd092SDavid Ahern if (!master_idx) 267221fdd092SDavid Ahern return false; 267321fdd092SDavid Ahern 2674aab456dfSEric Dumazet master = dev ? netdev_master_upper_dev_get(dev) : NULL; 2675d3432bf1SLahav Schlesinger 2676d3432bf1SLahav Schlesinger /* 0 is already used to denote NDA_MASTER wasn't passed, therefore need another 2677d3432bf1SLahav Schlesinger * invalid value for ifindex to denote "no master". 2678d3432bf1SLahav Schlesinger */ 2679d3432bf1SLahav Schlesinger if (master_idx == -1) 2680d3432bf1SLahav Schlesinger return !!master; 2681d3432bf1SLahav Schlesinger 268221fdd092SDavid Ahern if (!master || master->ifindex != master_idx) 268321fdd092SDavid Ahern return true; 268421fdd092SDavid Ahern 268521fdd092SDavid Ahern return false; 268621fdd092SDavid Ahern } 268721fdd092SDavid Ahern 268816660f0bSDavid Ahern static bool neigh_ifindex_filtered(struct net_device *dev, int filter_idx) 268916660f0bSDavid Ahern { 2690aab456dfSEric Dumazet if (filter_idx && (!dev || dev->ifindex != filter_idx)) 269116660f0bSDavid Ahern return true; 269216660f0bSDavid Ahern 269316660f0bSDavid Ahern return false; 269416660f0bSDavid Ahern } 269516660f0bSDavid Ahern 26966f52f80eSDavid Ahern struct neigh_dump_filter { 26976f52f80eSDavid Ahern int master_idx; 26986f52f80eSDavid Ahern int dev_idx; 26996f52f80eSDavid Ahern }; 27006f52f80eSDavid Ahern 27011da177e4SLinus Torvalds static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, 27026f52f80eSDavid Ahern struct netlink_callback *cb, 27036f52f80eSDavid Ahern struct neigh_dump_filter *filter) 27041da177e4SLinus Torvalds { 27053b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 27061da177e4SLinus Torvalds struct neighbour *n; 27071da177e4SLinus Torvalds int rc, h, s_h = cb->args[1]; 27081da177e4SLinus Torvalds int idx, s_idx = idx = cb->args[2]; 2709d6bf7817SEric Dumazet struct neigh_hash_table *nht; 271021fdd092SDavid Ahern unsigned int flags = NLM_F_MULTI; 271121fdd092SDavid Ahern 27126f52f80eSDavid Ahern if (filter->dev_idx || filter->master_idx) 271321fdd092SDavid Ahern flags |= NLM_F_DUMP_FILTERED; 27141da177e4SLinus Torvalds 2715d6bf7817SEric Dumazet rcu_read_lock_bh(); 2716d6bf7817SEric Dumazet nht = rcu_dereference_bh(tbl->nht); 2717d6bf7817SEric Dumazet 27184bd6683bSEric Dumazet for (h = s_h; h < (1 << nht->hash_shift); h++) { 27191da177e4SLinus Torvalds if (h > s_h) 27201da177e4SLinus Torvalds s_idx = 0; 2721767e97e1SEric Dumazet for (n = rcu_dereference_bh(nht->hash_buckets[h]), idx = 0; 2722767e97e1SEric Dumazet n != NULL; 2723767e97e1SEric Dumazet n = rcu_dereference_bh(n->next)) { 272418502acdSZhang Shengju if (idx < s_idx || !net_eq(dev_net(n->dev), net)) 272518502acdSZhang Shengju goto next; 27266f52f80eSDavid Ahern if (neigh_ifindex_filtered(n->dev, filter->dev_idx) || 27276f52f80eSDavid Ahern neigh_master_filtered(n->dev, filter->master_idx)) 2728efc683fcSGautam Kachroo goto next; 272915e47304SEric W. Biederman if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid, 27301da177e4SLinus Torvalds cb->nlh->nlmsg_seq, 2731b6544c0bSJamal Hadi Salim RTM_NEWNEIGH, 273221fdd092SDavid Ahern flags) < 0) { 27331da177e4SLinus Torvalds rc = -1; 27341da177e4SLinus Torvalds goto out; 27351da177e4SLinus Torvalds } 2736efc683fcSGautam Kachroo next: 2737efc683fcSGautam Kachroo idx++; 27381da177e4SLinus Torvalds } 27391da177e4SLinus Torvalds } 27401da177e4SLinus Torvalds rc = skb->len; 27411da177e4SLinus Torvalds out: 2742d6bf7817SEric Dumazet rcu_read_unlock_bh(); 27431da177e4SLinus Torvalds cb->args[1] = h; 27441da177e4SLinus Torvalds cb->args[2] = idx; 27451da177e4SLinus Torvalds return rc; 27461da177e4SLinus Torvalds } 27471da177e4SLinus Torvalds 274884920c14STony Zelenoff static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, 27496f52f80eSDavid Ahern struct netlink_callback *cb, 27506f52f80eSDavid Ahern struct neigh_dump_filter *filter) 275184920c14STony Zelenoff { 275284920c14STony Zelenoff struct pneigh_entry *n; 275384920c14STony Zelenoff struct net *net = sock_net(skb->sk); 275484920c14STony Zelenoff int rc, h, s_h = cb->args[3]; 275584920c14STony Zelenoff int idx, s_idx = idx = cb->args[4]; 27566f52f80eSDavid Ahern unsigned int flags = NLM_F_MULTI; 27576f52f80eSDavid Ahern 27586f52f80eSDavid Ahern if (filter->dev_idx || filter->master_idx) 27596f52f80eSDavid Ahern flags |= NLM_F_DUMP_FILTERED; 276084920c14STony Zelenoff 276184920c14STony Zelenoff read_lock_bh(&tbl->lock); 276284920c14STony Zelenoff 27634bd6683bSEric Dumazet for (h = s_h; h <= PNEIGH_HASHMASK; h++) { 276484920c14STony Zelenoff if (h > s_h) 276584920c14STony Zelenoff s_idx = 0; 276684920c14STony Zelenoff for (n = tbl->phash_buckets[h], idx = 0; n; n = n->next) { 276718502acdSZhang Shengju if (idx < s_idx || pneigh_net(n) != net) 276884920c14STony Zelenoff goto next; 27696f52f80eSDavid Ahern if (neigh_ifindex_filtered(n->dev, filter->dev_idx) || 27706f52f80eSDavid Ahern neigh_master_filtered(n->dev, filter->master_idx)) 27716f52f80eSDavid Ahern goto next; 277215e47304SEric W. Biederman if (pneigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid, 277384920c14STony Zelenoff cb->nlh->nlmsg_seq, 27746f52f80eSDavid Ahern RTM_NEWNEIGH, flags, tbl) < 0) { 277584920c14STony Zelenoff read_unlock_bh(&tbl->lock); 277684920c14STony Zelenoff rc = -1; 277784920c14STony Zelenoff goto out; 277884920c14STony Zelenoff } 277984920c14STony Zelenoff next: 278084920c14STony Zelenoff idx++; 278184920c14STony Zelenoff } 278284920c14STony Zelenoff } 278384920c14STony Zelenoff 278484920c14STony Zelenoff read_unlock_bh(&tbl->lock); 278584920c14STony Zelenoff rc = skb->len; 278684920c14STony Zelenoff out: 278784920c14STony Zelenoff cb->args[3] = h; 278884920c14STony Zelenoff cb->args[4] = idx; 278984920c14STony Zelenoff return rc; 279084920c14STony Zelenoff 279184920c14STony Zelenoff } 279284920c14STony Zelenoff 279351183d23SDavid Ahern static int neigh_valid_dump_req(const struct nlmsghdr *nlh, 279451183d23SDavid Ahern bool strict_check, 279551183d23SDavid Ahern struct neigh_dump_filter *filter, 279651183d23SDavid Ahern struct netlink_ext_ack *extack) 279751183d23SDavid Ahern { 279851183d23SDavid Ahern struct nlattr *tb[NDA_MAX + 1]; 279951183d23SDavid Ahern int err, i; 280051183d23SDavid Ahern 280151183d23SDavid Ahern if (strict_check) { 280251183d23SDavid Ahern struct ndmsg *ndm; 280351183d23SDavid Ahern 280451183d23SDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) { 280551183d23SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid header for neighbor dump request"); 280651183d23SDavid Ahern return -EINVAL; 280751183d23SDavid Ahern } 280851183d23SDavid Ahern 280951183d23SDavid Ahern ndm = nlmsg_data(nlh); 281051183d23SDavid Ahern if (ndm->ndm_pad1 || ndm->ndm_pad2 || ndm->ndm_ifindex || 2811c0fde870SDavid Ahern ndm->ndm_state || ndm->ndm_type) { 281251183d23SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor dump request"); 281351183d23SDavid Ahern return -EINVAL; 281451183d23SDavid Ahern } 281551183d23SDavid Ahern 2816c0fde870SDavid Ahern if (ndm->ndm_flags & ~NTF_PROXY) { 2817c0fde870SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor dump request"); 2818c0fde870SDavid Ahern return -EINVAL; 2819c0fde870SDavid Ahern } 2820c0fde870SDavid Ahern 28218cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct ndmsg), 28228cb08174SJohannes Berg tb, NDA_MAX, nda_policy, 28238cb08174SJohannes Berg extack); 282451183d23SDavid Ahern } else { 28258cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(struct ndmsg), tb, 28268cb08174SJohannes Berg NDA_MAX, nda_policy, extack); 282751183d23SDavid Ahern } 282851183d23SDavid Ahern if (err < 0) 282951183d23SDavid Ahern return err; 283051183d23SDavid Ahern 283151183d23SDavid Ahern for (i = 0; i <= NDA_MAX; ++i) { 283251183d23SDavid Ahern if (!tb[i]) 283351183d23SDavid Ahern continue; 283451183d23SDavid Ahern 283551183d23SDavid Ahern /* all new attributes should require strict_check */ 283651183d23SDavid Ahern switch (i) { 283751183d23SDavid Ahern case NDA_IFINDEX: 283851183d23SDavid Ahern filter->dev_idx = nla_get_u32(tb[i]); 283951183d23SDavid Ahern break; 284051183d23SDavid Ahern case NDA_MASTER: 284151183d23SDavid Ahern filter->master_idx = nla_get_u32(tb[i]); 284251183d23SDavid Ahern break; 284351183d23SDavid Ahern default: 284451183d23SDavid Ahern if (strict_check) { 284551183d23SDavid Ahern NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor dump request"); 284651183d23SDavid Ahern return -EINVAL; 284751183d23SDavid Ahern } 284851183d23SDavid Ahern } 284951183d23SDavid Ahern } 285051183d23SDavid Ahern 285151183d23SDavid Ahern return 0; 285251183d23SDavid Ahern } 285351183d23SDavid Ahern 2854c8822a4eSThomas Graf static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb) 28551da177e4SLinus Torvalds { 28566f52f80eSDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 28576f52f80eSDavid Ahern struct neigh_dump_filter filter = {}; 28581da177e4SLinus Torvalds struct neigh_table *tbl; 28591da177e4SLinus Torvalds int t, family, s_t; 286084920c14STony Zelenoff int proxy = 0; 28614bd6683bSEric Dumazet int err; 28621da177e4SLinus Torvalds 28636f52f80eSDavid Ahern family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family; 286484920c14STony Zelenoff 286584920c14STony Zelenoff /* check for full ndmsg structure presence, family member is 286684920c14STony Zelenoff * the same for both structures 286784920c14STony Zelenoff */ 28686f52f80eSDavid Ahern if (nlmsg_len(nlh) >= sizeof(struct ndmsg) && 28696f52f80eSDavid Ahern ((struct ndmsg *)nlmsg_data(nlh))->ndm_flags == NTF_PROXY) 287084920c14STony Zelenoff proxy = 1; 287184920c14STony Zelenoff 287251183d23SDavid Ahern err = neigh_valid_dump_req(nlh, cb->strict_check, &filter, cb->extack); 287351183d23SDavid Ahern if (err < 0 && cb->strict_check) 287451183d23SDavid Ahern return err; 287551183d23SDavid Ahern 28761da177e4SLinus Torvalds s_t = cb->args[0]; 28771da177e4SLinus Torvalds 2878d7480fd3SWANG Cong for (t = 0; t < NEIGH_NR_TABLES; t++) { 2879d7480fd3SWANG Cong tbl = neigh_tables[t]; 2880d7480fd3SWANG Cong 2881d7480fd3SWANG Cong if (!tbl) 2882d7480fd3SWANG Cong continue; 28831da177e4SLinus Torvalds if (t < s_t || (family && tbl->family != family)) 28841da177e4SLinus Torvalds continue; 28851da177e4SLinus Torvalds if (t > s_t) 28861da177e4SLinus Torvalds memset(&cb->args[1], 0, sizeof(cb->args) - 28871da177e4SLinus Torvalds sizeof(cb->args[0])); 288884920c14STony Zelenoff if (proxy) 28896f52f80eSDavid Ahern err = pneigh_dump_table(tbl, skb, cb, &filter); 289084920c14STony Zelenoff else 28916f52f80eSDavid Ahern err = neigh_dump_table(tbl, skb, cb, &filter); 28924bd6683bSEric Dumazet if (err < 0) 28934bd6683bSEric Dumazet break; 28941da177e4SLinus Torvalds } 28951da177e4SLinus Torvalds 28961da177e4SLinus Torvalds cb->args[0] = t; 28971da177e4SLinus Torvalds return skb->len; 28981da177e4SLinus Torvalds } 28991da177e4SLinus Torvalds 290082cbb5c6SRoopa Prabhu static int neigh_valid_get_req(const struct nlmsghdr *nlh, 290182cbb5c6SRoopa Prabhu struct neigh_table **tbl, 290282cbb5c6SRoopa Prabhu void **dst, int *dev_idx, u8 *ndm_flags, 290382cbb5c6SRoopa Prabhu struct netlink_ext_ack *extack) 290482cbb5c6SRoopa Prabhu { 290582cbb5c6SRoopa Prabhu struct nlattr *tb[NDA_MAX + 1]; 290682cbb5c6SRoopa Prabhu struct ndmsg *ndm; 290782cbb5c6SRoopa Prabhu int err, i; 290882cbb5c6SRoopa Prabhu 290982cbb5c6SRoopa Prabhu if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) { 291082cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Invalid header for neighbor get request"); 291182cbb5c6SRoopa Prabhu return -EINVAL; 291282cbb5c6SRoopa Prabhu } 291382cbb5c6SRoopa Prabhu 291482cbb5c6SRoopa Prabhu ndm = nlmsg_data(nlh); 291582cbb5c6SRoopa Prabhu if (ndm->ndm_pad1 || ndm->ndm_pad2 || ndm->ndm_state || 291682cbb5c6SRoopa Prabhu ndm->ndm_type) { 291782cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor get request"); 291882cbb5c6SRoopa Prabhu return -EINVAL; 291982cbb5c6SRoopa Prabhu } 292082cbb5c6SRoopa Prabhu 292182cbb5c6SRoopa Prabhu if (ndm->ndm_flags & ~NTF_PROXY) { 292282cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor get request"); 292382cbb5c6SRoopa Prabhu return -EINVAL; 292482cbb5c6SRoopa Prabhu } 292582cbb5c6SRoopa Prabhu 29268cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct ndmsg), tb, 29278cb08174SJohannes Berg NDA_MAX, nda_policy, extack); 292882cbb5c6SRoopa Prabhu if (err < 0) 292982cbb5c6SRoopa Prabhu return err; 293082cbb5c6SRoopa Prabhu 293182cbb5c6SRoopa Prabhu *ndm_flags = ndm->ndm_flags; 293282cbb5c6SRoopa Prabhu *dev_idx = ndm->ndm_ifindex; 293382cbb5c6SRoopa Prabhu *tbl = neigh_find_table(ndm->ndm_family); 293482cbb5c6SRoopa Prabhu if (*tbl == NULL) { 293582cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Unsupported family in header for neighbor get request"); 293682cbb5c6SRoopa Prabhu return -EAFNOSUPPORT; 293782cbb5c6SRoopa Prabhu } 293882cbb5c6SRoopa Prabhu 293982cbb5c6SRoopa Prabhu for (i = 0; i <= NDA_MAX; ++i) { 294082cbb5c6SRoopa Prabhu if (!tb[i]) 294182cbb5c6SRoopa Prabhu continue; 294282cbb5c6SRoopa Prabhu 294382cbb5c6SRoopa Prabhu switch (i) { 294482cbb5c6SRoopa Prabhu case NDA_DST: 294582cbb5c6SRoopa Prabhu if (nla_len(tb[i]) != (int)(*tbl)->key_len) { 294682cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Invalid network address in neighbor get request"); 294782cbb5c6SRoopa Prabhu return -EINVAL; 294882cbb5c6SRoopa Prabhu } 294982cbb5c6SRoopa Prabhu *dst = nla_data(tb[i]); 295082cbb5c6SRoopa Prabhu break; 295182cbb5c6SRoopa Prabhu default: 295282cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor get request"); 295382cbb5c6SRoopa Prabhu return -EINVAL; 295482cbb5c6SRoopa Prabhu } 295582cbb5c6SRoopa Prabhu } 295682cbb5c6SRoopa Prabhu 295782cbb5c6SRoopa Prabhu return 0; 295882cbb5c6SRoopa Prabhu } 295982cbb5c6SRoopa Prabhu 296082cbb5c6SRoopa Prabhu static inline size_t neigh_nlmsg_size(void) 296182cbb5c6SRoopa Prabhu { 296282cbb5c6SRoopa Prabhu return NLMSG_ALIGN(sizeof(struct ndmsg)) 296382cbb5c6SRoopa Prabhu + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */ 296482cbb5c6SRoopa Prabhu + nla_total_size(MAX_ADDR_LEN) /* NDA_LLADDR */ 296582cbb5c6SRoopa Prabhu + nla_total_size(sizeof(struct nda_cacheinfo)) 296682cbb5c6SRoopa Prabhu + nla_total_size(4) /* NDA_PROBES */ 29672c611ad9SRoopa Prabhu + nla_total_size(4) /* NDA_FLAGS_EXT */ 296882cbb5c6SRoopa Prabhu + nla_total_size(1); /* NDA_PROTOCOL */ 296982cbb5c6SRoopa Prabhu } 297082cbb5c6SRoopa Prabhu 297182cbb5c6SRoopa Prabhu static int neigh_get_reply(struct net *net, struct neighbour *neigh, 297282cbb5c6SRoopa Prabhu u32 pid, u32 seq) 297382cbb5c6SRoopa Prabhu { 297482cbb5c6SRoopa Prabhu struct sk_buff *skb; 297582cbb5c6SRoopa Prabhu int err = 0; 297682cbb5c6SRoopa Prabhu 297782cbb5c6SRoopa Prabhu skb = nlmsg_new(neigh_nlmsg_size(), GFP_KERNEL); 297882cbb5c6SRoopa Prabhu if (!skb) 297982cbb5c6SRoopa Prabhu return -ENOBUFS; 298082cbb5c6SRoopa Prabhu 298182cbb5c6SRoopa Prabhu err = neigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0); 298282cbb5c6SRoopa Prabhu if (err) { 298382cbb5c6SRoopa Prabhu kfree_skb(skb); 298482cbb5c6SRoopa Prabhu goto errout; 298582cbb5c6SRoopa Prabhu } 298682cbb5c6SRoopa Prabhu 298782cbb5c6SRoopa Prabhu err = rtnl_unicast(skb, net, pid); 298882cbb5c6SRoopa Prabhu errout: 298982cbb5c6SRoopa Prabhu return err; 299082cbb5c6SRoopa Prabhu } 299182cbb5c6SRoopa Prabhu 299282cbb5c6SRoopa Prabhu static inline size_t pneigh_nlmsg_size(void) 299382cbb5c6SRoopa Prabhu { 299482cbb5c6SRoopa Prabhu return NLMSG_ALIGN(sizeof(struct ndmsg)) 2995463561e6SColin Ian King + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */ 29962c611ad9SRoopa Prabhu + nla_total_size(4) /* NDA_FLAGS_EXT */ 299782cbb5c6SRoopa Prabhu + nla_total_size(1); /* NDA_PROTOCOL */ 299882cbb5c6SRoopa Prabhu } 299982cbb5c6SRoopa Prabhu 300082cbb5c6SRoopa Prabhu static int pneigh_get_reply(struct net *net, struct pneigh_entry *neigh, 300182cbb5c6SRoopa Prabhu u32 pid, u32 seq, struct neigh_table *tbl) 300282cbb5c6SRoopa Prabhu { 300382cbb5c6SRoopa Prabhu struct sk_buff *skb; 300482cbb5c6SRoopa Prabhu int err = 0; 300582cbb5c6SRoopa Prabhu 300682cbb5c6SRoopa Prabhu skb = nlmsg_new(pneigh_nlmsg_size(), GFP_KERNEL); 300782cbb5c6SRoopa Prabhu if (!skb) 300882cbb5c6SRoopa Prabhu return -ENOBUFS; 300982cbb5c6SRoopa Prabhu 301082cbb5c6SRoopa Prabhu err = pneigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0, tbl); 301182cbb5c6SRoopa Prabhu if (err) { 301282cbb5c6SRoopa Prabhu kfree_skb(skb); 301382cbb5c6SRoopa Prabhu goto errout; 301482cbb5c6SRoopa Prabhu } 301582cbb5c6SRoopa Prabhu 301682cbb5c6SRoopa Prabhu err = rtnl_unicast(skb, net, pid); 301782cbb5c6SRoopa Prabhu errout: 301882cbb5c6SRoopa Prabhu return err; 301982cbb5c6SRoopa Prabhu } 302082cbb5c6SRoopa Prabhu 302182cbb5c6SRoopa Prabhu static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh, 302282cbb5c6SRoopa Prabhu struct netlink_ext_ack *extack) 302382cbb5c6SRoopa Prabhu { 302482cbb5c6SRoopa Prabhu struct net *net = sock_net(in_skb->sk); 302582cbb5c6SRoopa Prabhu struct net_device *dev = NULL; 302682cbb5c6SRoopa Prabhu struct neigh_table *tbl = NULL; 302782cbb5c6SRoopa Prabhu struct neighbour *neigh; 302882cbb5c6SRoopa Prabhu void *dst = NULL; 302982cbb5c6SRoopa Prabhu u8 ndm_flags = 0; 303082cbb5c6SRoopa Prabhu int dev_idx = 0; 303182cbb5c6SRoopa Prabhu int err; 303282cbb5c6SRoopa Prabhu 303382cbb5c6SRoopa Prabhu err = neigh_valid_get_req(nlh, &tbl, &dst, &dev_idx, &ndm_flags, 303482cbb5c6SRoopa Prabhu extack); 303582cbb5c6SRoopa Prabhu if (err < 0) 303682cbb5c6SRoopa Prabhu return err; 303782cbb5c6SRoopa Prabhu 303882cbb5c6SRoopa Prabhu if (dev_idx) { 303982cbb5c6SRoopa Prabhu dev = __dev_get_by_index(net, dev_idx); 304082cbb5c6SRoopa Prabhu if (!dev) { 304182cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Unknown device ifindex"); 304282cbb5c6SRoopa Prabhu return -ENODEV; 304382cbb5c6SRoopa Prabhu } 304482cbb5c6SRoopa Prabhu } 304582cbb5c6SRoopa Prabhu 304682cbb5c6SRoopa Prabhu if (!dst) { 304782cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Network address not specified"); 304882cbb5c6SRoopa Prabhu return -EINVAL; 304982cbb5c6SRoopa Prabhu } 305082cbb5c6SRoopa Prabhu 305182cbb5c6SRoopa Prabhu if (ndm_flags & NTF_PROXY) { 305282cbb5c6SRoopa Prabhu struct pneigh_entry *pn; 305382cbb5c6SRoopa Prabhu 305482cbb5c6SRoopa Prabhu pn = pneigh_lookup(tbl, net, dst, dev, 0); 305582cbb5c6SRoopa Prabhu if (!pn) { 305682cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Proxy neighbour entry not found"); 305782cbb5c6SRoopa Prabhu return -ENOENT; 305882cbb5c6SRoopa Prabhu } 305982cbb5c6SRoopa Prabhu return pneigh_get_reply(net, pn, NETLINK_CB(in_skb).portid, 306082cbb5c6SRoopa Prabhu nlh->nlmsg_seq, tbl); 306182cbb5c6SRoopa Prabhu } 306282cbb5c6SRoopa Prabhu 306382cbb5c6SRoopa Prabhu if (!dev) { 306482cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "No device specified"); 306582cbb5c6SRoopa Prabhu return -EINVAL; 306682cbb5c6SRoopa Prabhu } 306782cbb5c6SRoopa Prabhu 306882cbb5c6SRoopa Prabhu neigh = neigh_lookup(tbl, dst, dev); 306982cbb5c6SRoopa Prabhu if (!neigh) { 307082cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Neighbour entry not found"); 307182cbb5c6SRoopa Prabhu return -ENOENT; 307282cbb5c6SRoopa Prabhu } 307382cbb5c6SRoopa Prabhu 307482cbb5c6SRoopa Prabhu err = neigh_get_reply(net, neigh, NETLINK_CB(in_skb).portid, 307582cbb5c6SRoopa Prabhu nlh->nlmsg_seq); 307682cbb5c6SRoopa Prabhu 307782cbb5c6SRoopa Prabhu neigh_release(neigh); 307882cbb5c6SRoopa Prabhu 307982cbb5c6SRoopa Prabhu return err; 308082cbb5c6SRoopa Prabhu } 308182cbb5c6SRoopa Prabhu 30821da177e4SLinus Torvalds void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie) 30831da177e4SLinus Torvalds { 30841da177e4SLinus Torvalds int chain; 3085d6bf7817SEric Dumazet struct neigh_hash_table *nht; 30861da177e4SLinus Torvalds 3087d6bf7817SEric Dumazet rcu_read_lock_bh(); 3088d6bf7817SEric Dumazet nht = rcu_dereference_bh(tbl->nht); 3089d6bf7817SEric Dumazet 3090767e97e1SEric Dumazet read_lock(&tbl->lock); /* avoid resizes */ 3091cd089336SDavid S. Miller for (chain = 0; chain < (1 << nht->hash_shift); chain++) { 30921da177e4SLinus Torvalds struct neighbour *n; 30931da177e4SLinus Torvalds 3094767e97e1SEric Dumazet for (n = rcu_dereference_bh(nht->hash_buckets[chain]); 3095767e97e1SEric Dumazet n != NULL; 3096767e97e1SEric Dumazet n = rcu_dereference_bh(n->next)) 30971da177e4SLinus Torvalds cb(n, cookie); 30981da177e4SLinus Torvalds } 3099d6bf7817SEric Dumazet read_unlock(&tbl->lock); 3100d6bf7817SEric Dumazet rcu_read_unlock_bh(); 31011da177e4SLinus Torvalds } 31021da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_for_each); 31031da177e4SLinus Torvalds 31041da177e4SLinus Torvalds /* The tbl->lock must be held as a writer and BH disabled. */ 31051da177e4SLinus Torvalds void __neigh_for_each_release(struct neigh_table *tbl, 31061da177e4SLinus Torvalds int (*cb)(struct neighbour *)) 31071da177e4SLinus Torvalds { 31081da177e4SLinus Torvalds int chain; 3109d6bf7817SEric Dumazet struct neigh_hash_table *nht; 31101da177e4SLinus Torvalds 3111d6bf7817SEric Dumazet nht = rcu_dereference_protected(tbl->nht, 3112d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 3113cd089336SDavid S. Miller for (chain = 0; chain < (1 << nht->hash_shift); chain++) { 3114767e97e1SEric Dumazet struct neighbour *n; 3115767e97e1SEric Dumazet struct neighbour __rcu **np; 31161da177e4SLinus Torvalds 3117d6bf7817SEric Dumazet np = &nht->hash_buckets[chain]; 3118767e97e1SEric Dumazet while ((n = rcu_dereference_protected(*np, 3119767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))) != NULL) { 31201da177e4SLinus Torvalds int release; 31211da177e4SLinus Torvalds 31221da177e4SLinus Torvalds write_lock(&n->lock); 31231da177e4SLinus Torvalds release = cb(n); 31241da177e4SLinus Torvalds if (release) { 3125767e97e1SEric Dumazet rcu_assign_pointer(*np, 3126767e97e1SEric Dumazet rcu_dereference_protected(n->next, 3127767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))); 312858956317SDavid Ahern neigh_mark_dead(n); 31291da177e4SLinus Torvalds } else 31301da177e4SLinus Torvalds np = &n->next; 31311da177e4SLinus Torvalds write_unlock(&n->lock); 31324f494554SThomas Graf if (release) 31334f494554SThomas Graf neigh_cleanup_and_release(n); 31341da177e4SLinus Torvalds } 31351da177e4SLinus Torvalds } 3136ecbb4169SAlexey Kuznetsov } 31371da177e4SLinus Torvalds EXPORT_SYMBOL(__neigh_for_each_release); 31381da177e4SLinus Torvalds 3139b79bda3dSEric W. Biederman int neigh_xmit(int index, struct net_device *dev, 31404fd3d7d9SEric W. Biederman const void *addr, struct sk_buff *skb) 31414fd3d7d9SEric W. Biederman { 3142b79bda3dSEric W. Biederman int err = -EAFNOSUPPORT; 3143b79bda3dSEric W. Biederman if (likely(index < NEIGH_NR_TABLES)) { 31444fd3d7d9SEric W. Biederman struct neigh_table *tbl; 31454fd3d7d9SEric W. Biederman struct neighbour *neigh; 31464fd3d7d9SEric W. Biederman 3147b79bda3dSEric W. Biederman tbl = neigh_tables[index]; 31484fd3d7d9SEric W. Biederman if (!tbl) 31494fd3d7d9SEric W. Biederman goto out; 3150b560f03dSDavid Barroso rcu_read_lock_bh(); 31514b2a2bfeSDavid Ahern if (index == NEIGH_ARP_TABLE) { 31524b2a2bfeSDavid Ahern u32 key = *((u32 *)addr); 31534b2a2bfeSDavid Ahern 31544b2a2bfeSDavid Ahern neigh = __ipv4_neigh_lookup_noref(dev, key); 31554b2a2bfeSDavid Ahern } else { 31564fd3d7d9SEric W. Biederman neigh = __neigh_lookup_noref(tbl, addr, dev); 31574b2a2bfeSDavid Ahern } 31584fd3d7d9SEric W. Biederman if (!neigh) 31594fd3d7d9SEric W. Biederman neigh = __neigh_create(tbl, addr, dev, false); 31604fd3d7d9SEric W. Biederman err = PTR_ERR(neigh); 3161b560f03dSDavid Barroso if (IS_ERR(neigh)) { 3162b560f03dSDavid Barroso rcu_read_unlock_bh(); 31634fd3d7d9SEric W. Biederman goto out_kfree_skb; 3164b560f03dSDavid Barroso } 31654fd3d7d9SEric W. Biederman err = neigh->output(neigh, skb); 3166b560f03dSDavid Barroso rcu_read_unlock_bh(); 31674fd3d7d9SEric W. Biederman } 3168b79bda3dSEric W. Biederman else if (index == NEIGH_LINK_TABLE) { 3169b79bda3dSEric W. Biederman err = dev_hard_header(skb, dev, ntohs(skb->protocol), 3170b79bda3dSEric W. Biederman addr, NULL, skb->len); 3171b79bda3dSEric W. Biederman if (err < 0) 3172b79bda3dSEric W. Biederman goto out_kfree_skb; 3173b79bda3dSEric W. Biederman err = dev_queue_xmit(skb); 3174b79bda3dSEric W. Biederman } 31754fd3d7d9SEric W. Biederman out: 31764fd3d7d9SEric W. Biederman return err; 31774fd3d7d9SEric W. Biederman out_kfree_skb: 31784fd3d7d9SEric W. Biederman kfree_skb(skb); 31794fd3d7d9SEric W. Biederman goto out; 31804fd3d7d9SEric W. Biederman } 31814fd3d7d9SEric W. Biederman EXPORT_SYMBOL(neigh_xmit); 31824fd3d7d9SEric W. Biederman 31831da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 31841da177e4SLinus Torvalds 31851da177e4SLinus Torvalds static struct neighbour *neigh_get_first(struct seq_file *seq) 31861da177e4SLinus Torvalds { 31871da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 31881218854aSYOSHIFUJI Hideaki struct net *net = seq_file_net(seq); 3189d6bf7817SEric Dumazet struct neigh_hash_table *nht = state->nht; 31901da177e4SLinus Torvalds struct neighbour *n = NULL; 3191f530eed6SColin Ian King int bucket; 31921da177e4SLinus Torvalds 31931da177e4SLinus Torvalds state->flags &= ~NEIGH_SEQ_IS_PNEIGH; 3194cd089336SDavid S. Miller for (bucket = 0; bucket < (1 << nht->hash_shift); bucket++) { 3195767e97e1SEric Dumazet n = rcu_dereference_bh(nht->hash_buckets[bucket]); 31961da177e4SLinus Torvalds 31971da177e4SLinus Torvalds while (n) { 3198878628fbSYOSHIFUJI Hideaki if (!net_eq(dev_net(n->dev), net)) 3199426b5303SEric W. Biederman goto next; 32001da177e4SLinus Torvalds if (state->neigh_sub_iter) { 32011da177e4SLinus Torvalds loff_t fakep = 0; 32021da177e4SLinus Torvalds void *v; 32031da177e4SLinus Torvalds 32041da177e4SLinus Torvalds v = state->neigh_sub_iter(state, n, &fakep); 32051da177e4SLinus Torvalds if (!v) 32061da177e4SLinus Torvalds goto next; 32071da177e4SLinus Torvalds } 32081da177e4SLinus Torvalds if (!(state->flags & NEIGH_SEQ_SKIP_NOARP)) 32091da177e4SLinus Torvalds break; 32101da177e4SLinus Torvalds if (n->nud_state & ~NUD_NOARP) 32111da177e4SLinus Torvalds break; 32121da177e4SLinus Torvalds next: 3213767e97e1SEric Dumazet n = rcu_dereference_bh(n->next); 32141da177e4SLinus Torvalds } 32151da177e4SLinus Torvalds 32161da177e4SLinus Torvalds if (n) 32171da177e4SLinus Torvalds break; 32181da177e4SLinus Torvalds } 32191da177e4SLinus Torvalds state->bucket = bucket; 32201da177e4SLinus Torvalds 32211da177e4SLinus Torvalds return n; 32221da177e4SLinus Torvalds } 32231da177e4SLinus Torvalds 32241da177e4SLinus Torvalds static struct neighbour *neigh_get_next(struct seq_file *seq, 32251da177e4SLinus Torvalds struct neighbour *n, 32261da177e4SLinus Torvalds loff_t *pos) 32271da177e4SLinus Torvalds { 32281da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 32291218854aSYOSHIFUJI Hideaki struct net *net = seq_file_net(seq); 3230d6bf7817SEric Dumazet struct neigh_hash_table *nht = state->nht; 32311da177e4SLinus Torvalds 32321da177e4SLinus Torvalds if (state->neigh_sub_iter) { 32331da177e4SLinus Torvalds void *v = state->neigh_sub_iter(state, n, pos); 32341da177e4SLinus Torvalds if (v) 32351da177e4SLinus Torvalds return n; 32361da177e4SLinus Torvalds } 3237767e97e1SEric Dumazet n = rcu_dereference_bh(n->next); 32381da177e4SLinus Torvalds 32391da177e4SLinus Torvalds while (1) { 32401da177e4SLinus Torvalds while (n) { 3241878628fbSYOSHIFUJI Hideaki if (!net_eq(dev_net(n->dev), net)) 3242426b5303SEric W. Biederman goto next; 32431da177e4SLinus Torvalds if (state->neigh_sub_iter) { 32441da177e4SLinus Torvalds void *v = state->neigh_sub_iter(state, n, pos); 32451da177e4SLinus Torvalds if (v) 32461da177e4SLinus Torvalds return n; 32471da177e4SLinus Torvalds goto next; 32481da177e4SLinus Torvalds } 32491da177e4SLinus Torvalds if (!(state->flags & NEIGH_SEQ_SKIP_NOARP)) 32501da177e4SLinus Torvalds break; 32511da177e4SLinus Torvalds 32521da177e4SLinus Torvalds if (n->nud_state & ~NUD_NOARP) 32531da177e4SLinus Torvalds break; 32541da177e4SLinus Torvalds next: 3255767e97e1SEric Dumazet n = rcu_dereference_bh(n->next); 32561da177e4SLinus Torvalds } 32571da177e4SLinus Torvalds 32581da177e4SLinus Torvalds if (n) 32591da177e4SLinus Torvalds break; 32601da177e4SLinus Torvalds 3261cd089336SDavid S. Miller if (++state->bucket >= (1 << nht->hash_shift)) 32621da177e4SLinus Torvalds break; 32631da177e4SLinus Torvalds 3264767e97e1SEric Dumazet n = rcu_dereference_bh(nht->hash_buckets[state->bucket]); 32651da177e4SLinus Torvalds } 32661da177e4SLinus Torvalds 32671da177e4SLinus Torvalds if (n && pos) 32681da177e4SLinus Torvalds --(*pos); 32691da177e4SLinus Torvalds return n; 32701da177e4SLinus Torvalds } 32711da177e4SLinus Torvalds 32721da177e4SLinus Torvalds static struct neighbour *neigh_get_idx(struct seq_file *seq, loff_t *pos) 32731da177e4SLinus Torvalds { 32741da177e4SLinus Torvalds struct neighbour *n = neigh_get_first(seq); 32751da177e4SLinus Torvalds 32761da177e4SLinus Torvalds if (n) { 3277745e2031SChris Larson --(*pos); 32781da177e4SLinus Torvalds while (*pos) { 32791da177e4SLinus Torvalds n = neigh_get_next(seq, n, pos); 32801da177e4SLinus Torvalds if (!n) 32811da177e4SLinus Torvalds break; 32821da177e4SLinus Torvalds } 32831da177e4SLinus Torvalds } 32841da177e4SLinus Torvalds return *pos ? NULL : n; 32851da177e4SLinus Torvalds } 32861da177e4SLinus Torvalds 32871da177e4SLinus Torvalds static struct pneigh_entry *pneigh_get_first(struct seq_file *seq) 32881da177e4SLinus Torvalds { 32891da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 32901218854aSYOSHIFUJI Hideaki struct net *net = seq_file_net(seq); 32911da177e4SLinus Torvalds struct neigh_table *tbl = state->tbl; 32921da177e4SLinus Torvalds struct pneigh_entry *pn = NULL; 329348de7c0cSYang Li int bucket; 32941da177e4SLinus Torvalds 32951da177e4SLinus Torvalds state->flags |= NEIGH_SEQ_IS_PNEIGH; 32961da177e4SLinus Torvalds for (bucket = 0; bucket <= PNEIGH_HASHMASK; bucket++) { 32971da177e4SLinus Torvalds pn = tbl->phash_buckets[bucket]; 3298878628fbSYOSHIFUJI Hideaki while (pn && !net_eq(pneigh_net(pn), net)) 3299426b5303SEric W. Biederman pn = pn->next; 33001da177e4SLinus Torvalds if (pn) 33011da177e4SLinus Torvalds break; 33021da177e4SLinus Torvalds } 33031da177e4SLinus Torvalds state->bucket = bucket; 33041da177e4SLinus Torvalds 33051da177e4SLinus Torvalds return pn; 33061da177e4SLinus Torvalds } 33071da177e4SLinus Torvalds 33081da177e4SLinus Torvalds static struct pneigh_entry *pneigh_get_next(struct seq_file *seq, 33091da177e4SLinus Torvalds struct pneigh_entry *pn, 33101da177e4SLinus Torvalds loff_t *pos) 33111da177e4SLinus Torvalds { 33121da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 33131218854aSYOSHIFUJI Hideaki struct net *net = seq_file_net(seq); 33141da177e4SLinus Torvalds struct neigh_table *tbl = state->tbl; 33151da177e4SLinus Torvalds 3316df07a94cSJorge Boncompte [DTI2] do { 33171da177e4SLinus Torvalds pn = pn->next; 3318df07a94cSJorge Boncompte [DTI2] } while (pn && !net_eq(pneigh_net(pn), net)); 3319df07a94cSJorge Boncompte [DTI2] 33201da177e4SLinus Torvalds while (!pn) { 33211da177e4SLinus Torvalds if (++state->bucket > PNEIGH_HASHMASK) 33221da177e4SLinus Torvalds break; 33231da177e4SLinus Torvalds pn = tbl->phash_buckets[state->bucket]; 3324878628fbSYOSHIFUJI Hideaki while (pn && !net_eq(pneigh_net(pn), net)) 3325426b5303SEric W. Biederman pn = pn->next; 33261da177e4SLinus Torvalds if (pn) 33271da177e4SLinus Torvalds break; 33281da177e4SLinus Torvalds } 33291da177e4SLinus Torvalds 33301da177e4SLinus Torvalds if (pn && pos) 33311da177e4SLinus Torvalds --(*pos); 33321da177e4SLinus Torvalds 33331da177e4SLinus Torvalds return pn; 33341da177e4SLinus Torvalds } 33351da177e4SLinus Torvalds 33361da177e4SLinus Torvalds static struct pneigh_entry *pneigh_get_idx(struct seq_file *seq, loff_t *pos) 33371da177e4SLinus Torvalds { 33381da177e4SLinus Torvalds struct pneigh_entry *pn = pneigh_get_first(seq); 33391da177e4SLinus Torvalds 33401da177e4SLinus Torvalds if (pn) { 3341745e2031SChris Larson --(*pos); 33421da177e4SLinus Torvalds while (*pos) { 33431da177e4SLinus Torvalds pn = pneigh_get_next(seq, pn, pos); 33441da177e4SLinus Torvalds if (!pn) 33451da177e4SLinus Torvalds break; 33461da177e4SLinus Torvalds } 33471da177e4SLinus Torvalds } 33481da177e4SLinus Torvalds return *pos ? NULL : pn; 33491da177e4SLinus Torvalds } 33501da177e4SLinus Torvalds 33511da177e4SLinus Torvalds static void *neigh_get_idx_any(struct seq_file *seq, loff_t *pos) 33521da177e4SLinus Torvalds { 33531da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 33541da177e4SLinus Torvalds void *rc; 3355745e2031SChris Larson loff_t idxpos = *pos; 33561da177e4SLinus Torvalds 3357745e2031SChris Larson rc = neigh_get_idx(seq, &idxpos); 33581da177e4SLinus Torvalds if (!rc && !(state->flags & NEIGH_SEQ_NEIGH_ONLY)) 3359745e2031SChris Larson rc = pneigh_get_idx(seq, &idxpos); 33601da177e4SLinus Torvalds 33611da177e4SLinus Torvalds return rc; 33621da177e4SLinus Torvalds } 33631da177e4SLinus Torvalds 33641da177e4SLinus Torvalds void *neigh_seq_start(struct seq_file *seq, loff_t *pos, struct neigh_table *tbl, unsigned int neigh_seq_flags) 3365f3e92cb8SEric Dumazet __acquires(tbl->lock) 3366d6bf7817SEric Dumazet __acquires(rcu_bh) 33671da177e4SLinus Torvalds { 33681da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 33691da177e4SLinus Torvalds 33701da177e4SLinus Torvalds state->tbl = tbl; 33711da177e4SLinus Torvalds state->bucket = 0; 33721da177e4SLinus Torvalds state->flags = (neigh_seq_flags & ~NEIGH_SEQ_IS_PNEIGH); 33731da177e4SLinus Torvalds 3374d6bf7817SEric Dumazet rcu_read_lock_bh(); 3375d6bf7817SEric Dumazet state->nht = rcu_dereference_bh(tbl->nht); 3376f3e92cb8SEric Dumazet read_lock(&tbl->lock); 3377767e97e1SEric Dumazet 3378745e2031SChris Larson return *pos ? neigh_get_idx_any(seq, pos) : SEQ_START_TOKEN; 33791da177e4SLinus Torvalds } 33801da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_seq_start); 33811da177e4SLinus Torvalds 33821da177e4SLinus Torvalds void *neigh_seq_next(struct seq_file *seq, void *v, loff_t *pos) 33831da177e4SLinus Torvalds { 33841da177e4SLinus Torvalds struct neigh_seq_state *state; 33851da177e4SLinus Torvalds void *rc; 33861da177e4SLinus Torvalds 33871da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 3388bff69732SChris Larson rc = neigh_get_first(seq); 33891da177e4SLinus Torvalds goto out; 33901da177e4SLinus Torvalds } 33911da177e4SLinus Torvalds 33921da177e4SLinus Torvalds state = seq->private; 33931da177e4SLinus Torvalds if (!(state->flags & NEIGH_SEQ_IS_PNEIGH)) { 33941da177e4SLinus Torvalds rc = neigh_get_next(seq, v, NULL); 33951da177e4SLinus Torvalds if (rc) 33961da177e4SLinus Torvalds goto out; 33971da177e4SLinus Torvalds if (!(state->flags & NEIGH_SEQ_NEIGH_ONLY)) 33981da177e4SLinus Torvalds rc = pneigh_get_first(seq); 33991da177e4SLinus Torvalds } else { 34001da177e4SLinus Torvalds BUG_ON(state->flags & NEIGH_SEQ_NEIGH_ONLY); 34011da177e4SLinus Torvalds rc = pneigh_get_next(seq, v, NULL); 34021da177e4SLinus Torvalds } 34031da177e4SLinus Torvalds out: 34041da177e4SLinus Torvalds ++(*pos); 34051da177e4SLinus Torvalds return rc; 34061da177e4SLinus Torvalds } 34071da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_seq_next); 34081da177e4SLinus Torvalds 34091da177e4SLinus Torvalds void neigh_seq_stop(struct seq_file *seq, void *v) 3410f3e92cb8SEric Dumazet __releases(tbl->lock) 3411d6bf7817SEric Dumazet __releases(rcu_bh) 34121da177e4SLinus Torvalds { 3413f3e92cb8SEric Dumazet struct neigh_seq_state *state = seq->private; 3414f3e92cb8SEric Dumazet struct neigh_table *tbl = state->tbl; 3415f3e92cb8SEric Dumazet 3416f3e92cb8SEric Dumazet read_unlock(&tbl->lock); 3417d6bf7817SEric Dumazet rcu_read_unlock_bh(); 34181da177e4SLinus Torvalds } 34191da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_seq_stop); 34201da177e4SLinus Torvalds 34211da177e4SLinus Torvalds /* statistics via seq_file */ 34221da177e4SLinus Torvalds 34231da177e4SLinus Torvalds static void *neigh_stat_seq_start(struct seq_file *seq, loff_t *pos) 34241da177e4SLinus Torvalds { 3425359745d7SMuchun Song struct neigh_table *tbl = pde_data(file_inode(seq->file)); 34261da177e4SLinus Torvalds int cpu; 34271da177e4SLinus Torvalds 34281da177e4SLinus Torvalds if (*pos == 0) 34291da177e4SLinus Torvalds return SEQ_START_TOKEN; 34301da177e4SLinus Torvalds 34310f23174aSRusty Russell for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) { 34321da177e4SLinus Torvalds if (!cpu_possible(cpu)) 34331da177e4SLinus Torvalds continue; 34341da177e4SLinus Torvalds *pos = cpu+1; 34351da177e4SLinus Torvalds return per_cpu_ptr(tbl->stats, cpu); 34361da177e4SLinus Torvalds } 34371da177e4SLinus Torvalds return NULL; 34381da177e4SLinus Torvalds } 34391da177e4SLinus Torvalds 34401da177e4SLinus Torvalds static void *neigh_stat_seq_next(struct seq_file *seq, void *v, loff_t *pos) 34411da177e4SLinus Torvalds { 3442359745d7SMuchun Song struct neigh_table *tbl = pde_data(file_inode(seq->file)); 34431da177e4SLinus Torvalds int cpu; 34441da177e4SLinus Torvalds 34450f23174aSRusty Russell for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) { 34461da177e4SLinus Torvalds if (!cpu_possible(cpu)) 34471da177e4SLinus Torvalds continue; 34481da177e4SLinus Torvalds *pos = cpu+1; 34491da177e4SLinus Torvalds return per_cpu_ptr(tbl->stats, cpu); 34501da177e4SLinus Torvalds } 34511e3f9f07SVasily Averin (*pos)++; 34521da177e4SLinus Torvalds return NULL; 34531da177e4SLinus Torvalds } 34541da177e4SLinus Torvalds 34551da177e4SLinus Torvalds static void neigh_stat_seq_stop(struct seq_file *seq, void *v) 34561da177e4SLinus Torvalds { 34571da177e4SLinus Torvalds 34581da177e4SLinus Torvalds } 34591da177e4SLinus Torvalds 34601da177e4SLinus Torvalds static int neigh_stat_seq_show(struct seq_file *seq, void *v) 34611da177e4SLinus Torvalds { 3462359745d7SMuchun Song struct neigh_table *tbl = pde_data(file_inode(seq->file)); 34631da177e4SLinus Torvalds struct neigh_statistics *st = v; 34641da177e4SLinus Torvalds 34651da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 34660547ffe6SYajun 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"); 34671da177e4SLinus Torvalds return 0; 34681da177e4SLinus Torvalds } 34691da177e4SLinus Torvalds 34701da177e4SLinus Torvalds seq_printf(seq, "%08x %08lx %08lx %08lx %08lx %08lx %08lx " 34710547ffe6SYajun Deng "%08lx %08lx %08lx " 34720547ffe6SYajun Deng "%08lx %08lx %08lx\n", 34731da177e4SLinus Torvalds atomic_read(&tbl->entries), 34741da177e4SLinus Torvalds 34751da177e4SLinus Torvalds st->allocs, 34761da177e4SLinus Torvalds st->destroys, 34771da177e4SLinus Torvalds st->hash_grows, 34781da177e4SLinus Torvalds 34791da177e4SLinus Torvalds st->lookups, 34801da177e4SLinus Torvalds st->hits, 34811da177e4SLinus Torvalds 34821da177e4SLinus Torvalds st->res_failed, 34831da177e4SLinus Torvalds 34841da177e4SLinus Torvalds st->rcv_probes_mcast, 34851da177e4SLinus Torvalds st->rcv_probes_ucast, 34861da177e4SLinus Torvalds 34871da177e4SLinus Torvalds st->periodic_gc_runs, 34889a6d276eSNeil Horman st->forced_gc_runs, 3489fb811395SRick Jones st->unres_discards, 3490fb811395SRick Jones st->table_fulls 34911da177e4SLinus Torvalds ); 34921da177e4SLinus Torvalds 34931da177e4SLinus Torvalds return 0; 34941da177e4SLinus Torvalds } 34951da177e4SLinus Torvalds 3496f690808eSStephen Hemminger static const struct seq_operations neigh_stat_seq_ops = { 34971da177e4SLinus Torvalds .start = neigh_stat_seq_start, 34981da177e4SLinus Torvalds .next = neigh_stat_seq_next, 34991da177e4SLinus Torvalds .stop = neigh_stat_seq_stop, 35001da177e4SLinus Torvalds .show = neigh_stat_seq_show, 35011da177e4SLinus Torvalds }; 35021da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 35031da177e4SLinus Torvalds 35047b8f7a40SRoopa Prabhu static void __neigh_notify(struct neighbour *n, int type, int flags, 35057b8f7a40SRoopa Prabhu u32 pid) 35061da177e4SLinus Torvalds { 3507c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(n->dev); 35088b8aec50SThomas Graf struct sk_buff *skb; 3509b8673311SThomas Graf int err = -ENOBUFS; 35101da177e4SLinus Torvalds 3511339bf98fSThomas Graf skb = nlmsg_new(neigh_nlmsg_size(), GFP_ATOMIC); 35128b8aec50SThomas Graf if (skb == NULL) 3513b8673311SThomas Graf goto errout; 35141da177e4SLinus Torvalds 35157b8f7a40SRoopa Prabhu err = neigh_fill_info(skb, n, pid, 0, type, flags); 351626932566SPatrick McHardy if (err < 0) { 351726932566SPatrick McHardy /* -EMSGSIZE implies BUG in neigh_nlmsg_size() */ 351826932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 351926932566SPatrick McHardy kfree_skb(skb); 352026932566SPatrick McHardy goto errout; 352126932566SPatrick McHardy } 35221ce85fe4SPablo Neira Ayuso rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); 35231ce85fe4SPablo Neira Ayuso return; 3524b8673311SThomas Graf errout: 3525b8673311SThomas Graf if (err < 0) 3526426b5303SEric W. Biederman rtnl_set_sk_err(net, RTNLGRP_NEIGH, err); 3527b8673311SThomas Graf } 3528b8673311SThomas Graf 3529b8673311SThomas Graf void neigh_app_ns(struct neighbour *n) 3530b8673311SThomas Graf { 35317b8f7a40SRoopa Prabhu __neigh_notify(n, RTM_GETNEIGH, NLM_F_REQUEST, 0); 35328b8aec50SThomas Graf } 35330a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_app_ns); 35341da177e4SLinus Torvalds 35351da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 3536b93196dcSCong Wang static int unres_qlen_max = INT_MAX / SKB_TRUESIZE(ETH_FRAME_LEN); 35371da177e4SLinus Torvalds 3538fe2c6338SJoe Perches static int proc_unres_qlen(struct ctl_table *ctl, int write, 353932927393SChristoph Hellwig void *buffer, size_t *lenp, loff_t *ppos) 35408b5c171bSEric Dumazet { 35418b5c171bSEric Dumazet int size, ret; 3542fe2c6338SJoe Perches struct ctl_table tmp = *ctl; 35438b5c171bSEric Dumazet 3544eec4844fSMatteo Croce tmp.extra1 = SYSCTL_ZERO; 3545ce46cc64SShan Wei tmp.extra2 = &unres_qlen_max; 35468b5c171bSEric Dumazet tmp.data = &size; 3547ce46cc64SShan Wei 3548ce46cc64SShan Wei size = *(int *)ctl->data / SKB_TRUESIZE(ETH_FRAME_LEN); 3549ce46cc64SShan Wei ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); 3550ce46cc64SShan Wei 35518b5c171bSEric Dumazet if (write && !ret) 35528b5c171bSEric Dumazet *(int *)ctl->data = size * SKB_TRUESIZE(ETH_FRAME_LEN); 35538b5c171bSEric Dumazet return ret; 35548b5c171bSEric Dumazet } 35558b5c171bSEric Dumazet 35561d4c8c29SJiri Pirko static void neigh_copy_dflt_parms(struct net *net, struct neigh_parms *p, 35571d4c8c29SJiri Pirko int index) 35581d4c8c29SJiri Pirko { 35591d4c8c29SJiri Pirko struct net_device *dev; 35601d4c8c29SJiri Pirko int family = neigh_parms_family(p); 35611d4c8c29SJiri Pirko 35621d4c8c29SJiri Pirko rcu_read_lock(); 35631d4c8c29SJiri Pirko for_each_netdev_rcu(net, dev) { 35641d4c8c29SJiri Pirko struct neigh_parms *dst_p = 35651d4c8c29SJiri Pirko neigh_get_dev_parms_rcu(dev, family); 35661d4c8c29SJiri Pirko 35671d4c8c29SJiri Pirko if (dst_p && !test_bit(index, dst_p->data_state)) 35681d4c8c29SJiri Pirko dst_p->data[index] = p->data[index]; 35691d4c8c29SJiri Pirko } 35701d4c8c29SJiri Pirko rcu_read_unlock(); 35711d4c8c29SJiri Pirko } 35721d4c8c29SJiri Pirko 35731d4c8c29SJiri Pirko static void neigh_proc_update(struct ctl_table *ctl, int write) 35741d4c8c29SJiri Pirko { 35751d4c8c29SJiri Pirko struct net_device *dev = ctl->extra1; 35761d4c8c29SJiri Pirko struct neigh_parms *p = ctl->extra2; 357777d47afbSJiri Pirko struct net *net = neigh_parms_net(p); 35781d4c8c29SJiri Pirko int index = (int *) ctl->data - p->data; 35791d4c8c29SJiri Pirko 35801d4c8c29SJiri Pirko if (!write) 35811d4c8c29SJiri Pirko return; 35821d4c8c29SJiri Pirko 35831d4c8c29SJiri Pirko set_bit(index, p->data_state); 35847627ae60SMarcus Huewe if (index == NEIGH_VAR_DELAY_PROBE_TIME) 35852a4501aeSIdo Schimmel call_netevent_notifiers(NETEVENT_DELAY_PROBE_TIME_UPDATE, p); 35861d4c8c29SJiri Pirko if (!dev) /* NULL dev means this is default value */ 35871d4c8c29SJiri Pirko neigh_copy_dflt_parms(net, p, index); 35881d4c8c29SJiri Pirko } 35891d4c8c29SJiri Pirko 35901f9248e5SJiri Pirko static int neigh_proc_dointvec_zero_intmax(struct ctl_table *ctl, int write, 359132927393SChristoph Hellwig void *buffer, size_t *lenp, 359232927393SChristoph Hellwig loff_t *ppos) 35931f9248e5SJiri Pirko { 35941f9248e5SJiri Pirko struct ctl_table tmp = *ctl; 35951d4c8c29SJiri Pirko int ret; 35961f9248e5SJiri Pirko 3597eec4844fSMatteo Croce tmp.extra1 = SYSCTL_ZERO; 3598eec4844fSMatteo Croce tmp.extra2 = SYSCTL_INT_MAX; 35991f9248e5SJiri Pirko 36001d4c8c29SJiri Pirko ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); 36011d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 36021d4c8c29SJiri Pirko return ret; 36031f9248e5SJiri Pirko } 36041f9248e5SJiri Pirko 3605211da42eSYuwei Wang static int neigh_proc_dointvec_ms_jiffies_positive(struct ctl_table *ctl, int write, 3606211da42eSYuwei Wang void *buffer, size_t *lenp, loff_t *ppos) 3607211da42eSYuwei Wang { 3608211da42eSYuwei Wang struct ctl_table tmp = *ctl; 3609211da42eSYuwei Wang int ret; 3610211da42eSYuwei Wang 3611211da42eSYuwei Wang int min = msecs_to_jiffies(1); 3612211da42eSYuwei Wang 3613211da42eSYuwei Wang tmp.extra1 = &min; 3614211da42eSYuwei Wang tmp.extra2 = NULL; 3615211da42eSYuwei Wang 3616211da42eSYuwei Wang ret = proc_dointvec_ms_jiffies_minmax(&tmp, write, buffer, lenp, ppos); 3617211da42eSYuwei Wang neigh_proc_update(ctl, write); 3618211da42eSYuwei Wang return ret; 3619211da42eSYuwei Wang } 3620211da42eSYuwei Wang 362132927393SChristoph Hellwig int neigh_proc_dointvec(struct ctl_table *ctl, int write, void *buffer, 362232927393SChristoph Hellwig size_t *lenp, loff_t *ppos) 3623cb5b09c1SJiri Pirko { 36241d4c8c29SJiri Pirko int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 36251d4c8c29SJiri Pirko 36261d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 36271d4c8c29SJiri Pirko return ret; 3628cb5b09c1SJiri Pirko } 3629cb5b09c1SJiri Pirko EXPORT_SYMBOL(neigh_proc_dointvec); 3630cb5b09c1SJiri Pirko 363132927393SChristoph Hellwig int neigh_proc_dointvec_jiffies(struct ctl_table *ctl, int write, void *buffer, 3632cb5b09c1SJiri Pirko size_t *lenp, loff_t *ppos) 3633cb5b09c1SJiri Pirko { 36341d4c8c29SJiri Pirko int ret = proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos); 36351d4c8c29SJiri Pirko 36361d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 36371d4c8c29SJiri Pirko return ret; 3638cb5b09c1SJiri Pirko } 3639cb5b09c1SJiri Pirko EXPORT_SYMBOL(neigh_proc_dointvec_jiffies); 3640cb5b09c1SJiri Pirko 3641cb5b09c1SJiri Pirko static int neigh_proc_dointvec_userhz_jiffies(struct ctl_table *ctl, int write, 364232927393SChristoph Hellwig void *buffer, size_t *lenp, 364332927393SChristoph Hellwig loff_t *ppos) 3644cb5b09c1SJiri Pirko { 36451d4c8c29SJiri Pirko int ret = proc_dointvec_userhz_jiffies(ctl, write, buffer, lenp, ppos); 36461d4c8c29SJiri Pirko 36471d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 36481d4c8c29SJiri Pirko return ret; 3649cb5b09c1SJiri Pirko } 3650cb5b09c1SJiri Pirko 3651cb5b09c1SJiri Pirko int neigh_proc_dointvec_ms_jiffies(struct ctl_table *ctl, int write, 365232927393SChristoph Hellwig void *buffer, size_t *lenp, loff_t *ppos) 3653cb5b09c1SJiri Pirko { 36541d4c8c29SJiri Pirko int ret = proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos); 36551d4c8c29SJiri Pirko 36561d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 36571d4c8c29SJiri Pirko return ret; 3658cb5b09c1SJiri Pirko } 3659cb5b09c1SJiri Pirko EXPORT_SYMBOL(neigh_proc_dointvec_ms_jiffies); 3660cb5b09c1SJiri Pirko 3661cb5b09c1SJiri Pirko static int neigh_proc_dointvec_unres_qlen(struct ctl_table *ctl, int write, 366232927393SChristoph Hellwig void *buffer, size_t *lenp, 366332927393SChristoph Hellwig loff_t *ppos) 3664cb5b09c1SJiri Pirko { 36651d4c8c29SJiri Pirko int ret = proc_unres_qlen(ctl, write, buffer, lenp, ppos); 36661d4c8c29SJiri Pirko 36671d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 36681d4c8c29SJiri Pirko return ret; 3669cb5b09c1SJiri Pirko } 3670cb5b09c1SJiri Pirko 36714bf6980dSJean-Francois Remy static int neigh_proc_base_reachable_time(struct ctl_table *ctl, int write, 367232927393SChristoph Hellwig void *buffer, size_t *lenp, 367332927393SChristoph Hellwig loff_t *ppos) 36744bf6980dSJean-Francois Remy { 36754bf6980dSJean-Francois Remy struct neigh_parms *p = ctl->extra2; 36764bf6980dSJean-Francois Remy int ret; 36774bf6980dSJean-Francois Remy 36784bf6980dSJean-Francois Remy if (strcmp(ctl->procname, "base_reachable_time") == 0) 36794bf6980dSJean-Francois Remy ret = neigh_proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos); 36804bf6980dSJean-Francois Remy else if (strcmp(ctl->procname, "base_reachable_time_ms") == 0) 36814bf6980dSJean-Francois Remy ret = neigh_proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos); 36824bf6980dSJean-Francois Remy else 36834bf6980dSJean-Francois Remy ret = -1; 36844bf6980dSJean-Francois Remy 36854bf6980dSJean-Francois Remy if (write && ret == 0) { 36864bf6980dSJean-Francois Remy /* update reachable_time as well, otherwise, the change will 36874bf6980dSJean-Francois Remy * only be effective after the next time neigh_periodic_work 36884bf6980dSJean-Francois Remy * decides to recompute it 36894bf6980dSJean-Francois Remy */ 36904bf6980dSJean-Francois Remy p->reachable_time = 36914bf6980dSJean-Francois Remy neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); 36924bf6980dSJean-Francois Remy } 36934bf6980dSJean-Francois Remy return ret; 36944bf6980dSJean-Francois Remy } 36954bf6980dSJean-Francois Remy 36961f9248e5SJiri Pirko #define NEIGH_PARMS_DATA_OFFSET(index) \ 36971f9248e5SJiri Pirko (&((struct neigh_parms *) 0)->data[index]) 36981f9248e5SJiri Pirko 36991f9248e5SJiri Pirko #define NEIGH_SYSCTL_ENTRY(attr, data_attr, name, mval, proc) \ 37001f9248e5SJiri Pirko [NEIGH_VAR_ ## attr] = { \ 37011f9248e5SJiri Pirko .procname = name, \ 37021f9248e5SJiri Pirko .data = NEIGH_PARMS_DATA_OFFSET(NEIGH_VAR_ ## data_attr), \ 37031f9248e5SJiri Pirko .maxlen = sizeof(int), \ 37041f9248e5SJiri Pirko .mode = mval, \ 37051f9248e5SJiri Pirko .proc_handler = proc, \ 37061f9248e5SJiri Pirko } 37071f9248e5SJiri Pirko 37081f9248e5SJiri Pirko #define NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(attr, name) \ 37091f9248e5SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_zero_intmax) 37101f9248e5SJiri Pirko 37111f9248e5SJiri Pirko #define NEIGH_SYSCTL_JIFFIES_ENTRY(attr, name) \ 3712cb5b09c1SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_jiffies) 37131f9248e5SJiri Pirko 37141f9248e5SJiri Pirko #define NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(attr, name) \ 3715cb5b09c1SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_userhz_jiffies) 37161f9248e5SJiri Pirko 3717211da42eSYuwei Wang #define NEIGH_SYSCTL_MS_JIFFIES_POSITIVE_ENTRY(attr, name) \ 3718211da42eSYuwei Wang NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_ms_jiffies_positive) 3719211da42eSYuwei Wang 37201f9248e5SJiri Pirko #define NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(attr, data_attr, name) \ 3721cb5b09c1SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_ms_jiffies) 37221f9248e5SJiri Pirko 37231f9248e5SJiri Pirko #define NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(attr, data_attr, name) \ 3724cb5b09c1SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_unres_qlen) 372554716e3bSEric W. Biederman 37261da177e4SLinus Torvalds static struct neigh_sysctl_table { 37271da177e4SLinus Torvalds struct ctl_table_header *sysctl_header; 37288b5c171bSEric Dumazet struct ctl_table neigh_vars[NEIGH_VAR_MAX + 1]; 3729ab32ea5dSBrian Haley } neigh_sysctl_template __read_mostly = { 37301da177e4SLinus Torvalds .neigh_vars = { 37311f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(MCAST_PROBES, "mcast_solicit"), 37321f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(UCAST_PROBES, "ucast_solicit"), 37331f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(APP_PROBES, "app_solicit"), 37348da86466SYOSHIFUJI Hideaki/吉藤英明 NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(MCAST_REPROBES, "mcast_resolicit"), 37351f9248e5SJiri Pirko NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(RETRANS_TIME, "retrans_time"), 37361f9248e5SJiri Pirko NEIGH_SYSCTL_JIFFIES_ENTRY(BASE_REACHABLE_TIME, "base_reachable_time"), 37371f9248e5SJiri Pirko NEIGH_SYSCTL_JIFFIES_ENTRY(DELAY_PROBE_TIME, "delay_first_probe_time"), 3738211da42eSYuwei Wang NEIGH_SYSCTL_MS_JIFFIES_POSITIVE_ENTRY(INTERVAL_PROBE_TIME_MS, 3739211da42eSYuwei Wang "interval_probe_time_ms"), 37401f9248e5SJiri Pirko NEIGH_SYSCTL_JIFFIES_ENTRY(GC_STALETIME, "gc_stale_time"), 37411f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(QUEUE_LEN_BYTES, "unres_qlen_bytes"), 37421f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(PROXY_QLEN, "proxy_qlen"), 37431f9248e5SJiri Pirko NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(ANYCAST_DELAY, "anycast_delay"), 37441f9248e5SJiri Pirko NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(PROXY_DELAY, "proxy_delay"), 37451f9248e5SJiri Pirko NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(LOCKTIME, "locktime"), 37461f9248e5SJiri Pirko NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(QUEUE_LEN, QUEUE_LEN_BYTES, "unres_qlen"), 37471f9248e5SJiri Pirko NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(RETRANS_TIME_MS, RETRANS_TIME, "retrans_time_ms"), 37481f9248e5SJiri Pirko NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(BASE_REACHABLE_TIME_MS, BASE_REACHABLE_TIME, "base_reachable_time_ms"), 37498b5c171bSEric Dumazet [NEIGH_VAR_GC_INTERVAL] = { 37501da177e4SLinus Torvalds .procname = "gc_interval", 37511da177e4SLinus Torvalds .maxlen = sizeof(int), 37521da177e4SLinus Torvalds .mode = 0644, 37536d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 37541da177e4SLinus Torvalds }, 37558b5c171bSEric Dumazet [NEIGH_VAR_GC_THRESH1] = { 37561da177e4SLinus Torvalds .procname = "gc_thresh1", 37571da177e4SLinus Torvalds .maxlen = sizeof(int), 37581da177e4SLinus Torvalds .mode = 0644, 3759eec4844fSMatteo Croce .extra1 = SYSCTL_ZERO, 3760eec4844fSMatteo Croce .extra2 = SYSCTL_INT_MAX, 3761555445cdSFrancesco Fusco .proc_handler = proc_dointvec_minmax, 37621da177e4SLinus Torvalds }, 37638b5c171bSEric Dumazet [NEIGH_VAR_GC_THRESH2] = { 37641da177e4SLinus Torvalds .procname = "gc_thresh2", 37651da177e4SLinus Torvalds .maxlen = sizeof(int), 37661da177e4SLinus Torvalds .mode = 0644, 3767eec4844fSMatteo Croce .extra1 = SYSCTL_ZERO, 3768eec4844fSMatteo Croce .extra2 = SYSCTL_INT_MAX, 3769555445cdSFrancesco Fusco .proc_handler = proc_dointvec_minmax, 37701da177e4SLinus Torvalds }, 37718b5c171bSEric Dumazet [NEIGH_VAR_GC_THRESH3] = { 37721da177e4SLinus Torvalds .procname = "gc_thresh3", 37731da177e4SLinus Torvalds .maxlen = sizeof(int), 37741da177e4SLinus Torvalds .mode = 0644, 3775eec4844fSMatteo Croce .extra1 = SYSCTL_ZERO, 3776eec4844fSMatteo Croce .extra2 = SYSCTL_INT_MAX, 3777555445cdSFrancesco Fusco .proc_handler = proc_dointvec_minmax, 37781da177e4SLinus Torvalds }, 3779c3bac5a7SPavel Emelyanov {}, 37801da177e4SLinus Torvalds }, 37811da177e4SLinus Torvalds }; 37821da177e4SLinus Torvalds 37831da177e4SLinus Torvalds int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, 378473af614aSJiri Pirko proc_handler *handler) 37851da177e4SLinus Torvalds { 37861f9248e5SJiri Pirko int i; 37873c607bbbSPavel Emelyanov struct neigh_sysctl_table *t; 37881f9248e5SJiri Pirko const char *dev_name_source; 37898f40a1f9SEric W. Biederman char neigh_path[ sizeof("net//neigh/") + IFNAMSIZ + IFNAMSIZ ]; 379073af614aSJiri Pirko char *p_name; 37911da177e4SLinus Torvalds 3792425b9c7fSVasily Averin t = kmemdup(&neigh_sysctl_template, sizeof(*t), GFP_KERNEL_ACCOUNT); 37931da177e4SLinus Torvalds if (!t) 37943c607bbbSPavel Emelyanov goto err; 37953c607bbbSPavel Emelyanov 3796b194c1f1SJiri Pirko for (i = 0; i < NEIGH_VAR_GC_INTERVAL; i++) { 37971f9248e5SJiri Pirko t->neigh_vars[i].data += (long) p; 3798cb5b09c1SJiri Pirko t->neigh_vars[i].extra1 = dev; 37991d4c8c29SJiri Pirko t->neigh_vars[i].extra2 = p; 3800cb5b09c1SJiri Pirko } 38011da177e4SLinus Torvalds 38021da177e4SLinus Torvalds if (dev) { 38031da177e4SLinus Torvalds dev_name_source = dev->name; 3804d12af679SEric W. Biederman /* Terminate the table early */ 38058b5c171bSEric Dumazet memset(&t->neigh_vars[NEIGH_VAR_GC_INTERVAL], 0, 38068b5c171bSEric Dumazet sizeof(t->neigh_vars[NEIGH_VAR_GC_INTERVAL])); 38071da177e4SLinus Torvalds } else { 38089ecf07a1SMathias Krause struct neigh_table *tbl = p->tbl; 38098f40a1f9SEric W. Biederman dev_name_source = "default"; 38109ecf07a1SMathias Krause t->neigh_vars[NEIGH_VAR_GC_INTERVAL].data = &tbl->gc_interval; 38119ecf07a1SMathias Krause t->neigh_vars[NEIGH_VAR_GC_THRESH1].data = &tbl->gc_thresh1; 38129ecf07a1SMathias Krause t->neigh_vars[NEIGH_VAR_GC_THRESH2].data = &tbl->gc_thresh2; 38139ecf07a1SMathias Krause t->neigh_vars[NEIGH_VAR_GC_THRESH3].data = &tbl->gc_thresh3; 38141da177e4SLinus Torvalds } 38151da177e4SLinus Torvalds 3816f8572d8fSEric W. Biederman if (handler) { 38171da177e4SLinus Torvalds /* RetransTime */ 38188b5c171bSEric Dumazet t->neigh_vars[NEIGH_VAR_RETRANS_TIME].proc_handler = handler; 38191da177e4SLinus Torvalds /* ReachableTime */ 38208b5c171bSEric Dumazet t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].proc_handler = handler; 38211da177e4SLinus Torvalds /* RetransTime (in milliseconds)*/ 38228b5c171bSEric Dumazet t->neigh_vars[NEIGH_VAR_RETRANS_TIME_MS].proc_handler = handler; 38231da177e4SLinus Torvalds /* ReachableTime (in milliseconds) */ 38248b5c171bSEric Dumazet t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].proc_handler = handler; 38254bf6980dSJean-Francois Remy } else { 38264bf6980dSJean-Francois Remy /* Those handlers will update p->reachable_time after 38274bf6980dSJean-Francois Remy * base_reachable_time(_ms) is set to ensure the new timer starts being 38284bf6980dSJean-Francois Remy * applied after the next neighbour update instead of waiting for 38294bf6980dSJean-Francois Remy * neigh_periodic_work to update its value (can be multiple minutes) 38304bf6980dSJean-Francois Remy * So any handler that replaces them should do this as well 38314bf6980dSJean-Francois Remy */ 38324bf6980dSJean-Francois Remy /* ReachableTime */ 38334bf6980dSJean-Francois Remy t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].proc_handler = 38344bf6980dSJean-Francois Remy neigh_proc_base_reachable_time; 38354bf6980dSJean-Francois Remy /* ReachableTime (in milliseconds) */ 38364bf6980dSJean-Francois Remy t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].proc_handler = 38374bf6980dSJean-Francois Remy neigh_proc_base_reachable_time; 38381da177e4SLinus Torvalds } 38391da177e4SLinus Torvalds 384073af614aSJiri Pirko switch (neigh_parms_family(p)) { 384173af614aSJiri Pirko case AF_INET: 384273af614aSJiri Pirko p_name = "ipv4"; 384373af614aSJiri Pirko break; 384473af614aSJiri Pirko case AF_INET6: 384573af614aSJiri Pirko p_name = "ipv6"; 384673af614aSJiri Pirko break; 384773af614aSJiri Pirko default: 384873af614aSJiri Pirko BUG(); 384973af614aSJiri Pirko } 385073af614aSJiri Pirko 38518f40a1f9SEric W. Biederman snprintf(neigh_path, sizeof(neigh_path), "net/%s/neigh/%s", 38528f40a1f9SEric W. Biederman p_name, dev_name_source); 38534ab438fcSDenis V. Lunev t->sysctl_header = 38548f40a1f9SEric W. Biederman register_net_sysctl(neigh_parms_net(p), neigh_path, t->neigh_vars); 38553c607bbbSPavel Emelyanov if (!t->sysctl_header) 38568f40a1f9SEric W. Biederman goto free; 38573c607bbbSPavel Emelyanov 38581da177e4SLinus Torvalds p->sysctl_table = t; 38591da177e4SLinus Torvalds return 0; 38601da177e4SLinus Torvalds 38611da177e4SLinus Torvalds free: 38621da177e4SLinus Torvalds kfree(t); 38633c607bbbSPavel Emelyanov err: 38643c607bbbSPavel Emelyanov return -ENOBUFS; 38651da177e4SLinus Torvalds } 38660a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_sysctl_register); 38671da177e4SLinus Torvalds 38681da177e4SLinus Torvalds void neigh_sysctl_unregister(struct neigh_parms *p) 38691da177e4SLinus Torvalds { 38701da177e4SLinus Torvalds if (p->sysctl_table) { 38711da177e4SLinus Torvalds struct neigh_sysctl_table *t = p->sysctl_table; 38721da177e4SLinus Torvalds p->sysctl_table = NULL; 38735dd3df10SEric W. Biederman unregister_net_sysctl_table(t->sysctl_header); 38741da177e4SLinus Torvalds kfree(t); 38751da177e4SLinus Torvalds } 38761da177e4SLinus Torvalds } 38770a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_sysctl_unregister); 38781da177e4SLinus Torvalds 38791da177e4SLinus Torvalds #endif /* CONFIG_SYSCTL */ 38801da177e4SLinus Torvalds 3881c8822a4eSThomas Graf static int __init neigh_init(void) 3882c8822a4eSThomas Graf { 3883b97bac64SFlorian Westphal rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL, 0); 3884b97bac64SFlorian Westphal rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL, 0); 388582cbb5c6SRoopa Prabhu rtnl_register(PF_UNSPEC, RTM_GETNEIGH, neigh_get, neigh_dump_info, 0); 3886c8822a4eSThomas Graf 3887c7ac8679SGreg Rose rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info, 3888b97bac64SFlorian Westphal 0); 3889b97bac64SFlorian Westphal rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL, 0); 3890c8822a4eSThomas Graf 3891c8822a4eSThomas Graf return 0; 3892c8822a4eSThomas Graf } 3893c8822a4eSThomas Graf 3894c8822a4eSThomas Graf subsys_initcall(neigh_init); 3895