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 { 11463862b5bSAruna-Hewapathirane return base ? (prandom_u32() % 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 3101da177e4SLinus Torvalds static void pneigh_queue_purge(struct sk_buff_head *list) 3111da177e4SLinus Torvalds { 3121da177e4SLinus Torvalds struct sk_buff *skb; 3131da177e4SLinus Torvalds 3141da177e4SLinus Torvalds while ((skb = skb_dequeue(list)) != NULL) { 3151da177e4SLinus Torvalds dev_put(skb->dev); 3161da177e4SLinus Torvalds kfree_skb(skb); 3171da177e4SLinus Torvalds } 3181da177e4SLinus Torvalds } 3191da177e4SLinus Torvalds 320859bd2efSDavid Ahern static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev, 321859bd2efSDavid Ahern bool skip_perm) 3221da177e4SLinus Torvalds { 3231da177e4SLinus Torvalds int i; 324d6bf7817SEric Dumazet struct neigh_hash_table *nht; 3251da177e4SLinus Torvalds 326d6bf7817SEric Dumazet nht = rcu_dereference_protected(tbl->nht, 327d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 328d6bf7817SEric Dumazet 329cd089336SDavid S. Miller for (i = 0; i < (1 << nht->hash_shift); i++) { 330767e97e1SEric Dumazet struct neighbour *n; 331767e97e1SEric Dumazet struct neighbour __rcu **np = &nht->hash_buckets[i]; 3321da177e4SLinus Torvalds 333767e97e1SEric Dumazet while ((n = rcu_dereference_protected(*np, 334767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))) != NULL) { 3351da177e4SLinus Torvalds if (dev && n->dev != dev) { 3361da177e4SLinus Torvalds np = &n->next; 3371da177e4SLinus Torvalds continue; 3381da177e4SLinus Torvalds } 339859bd2efSDavid Ahern if (skip_perm && n->nud_state & NUD_PERMANENT) { 340859bd2efSDavid Ahern np = &n->next; 341859bd2efSDavid Ahern continue; 342859bd2efSDavid Ahern } 343767e97e1SEric Dumazet rcu_assign_pointer(*np, 344767e97e1SEric Dumazet rcu_dereference_protected(n->next, 345767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))); 3461da177e4SLinus Torvalds write_lock(&n->lock); 3471da177e4SLinus Torvalds neigh_del_timer(n); 34858956317SDavid Ahern neigh_mark_dead(n); 3499f237430SReshetova, Elena if (refcount_read(&n->refcnt) != 1) { 3501da177e4SLinus Torvalds /* The most unpleasant situation. 3511da177e4SLinus Torvalds We must destroy neighbour entry, 3521da177e4SLinus Torvalds but someone still uses it. 3531da177e4SLinus Torvalds 3541da177e4SLinus Torvalds The destroy will be delayed until 3551da177e4SLinus Torvalds the last user releases us, but 3561da177e4SLinus Torvalds we must kill timers etc. and move 3571da177e4SLinus Torvalds it to safe state. 3581da177e4SLinus Torvalds */ 359c9ab4d85SEric Dumazet __skb_queue_purge(&n->arp_queue); 3608b5c171bSEric Dumazet n->arp_queue_len_bytes = 0; 3611da177e4SLinus Torvalds n->output = neigh_blackhole; 3621da177e4SLinus Torvalds if (n->nud_state & NUD_VALID) 3631da177e4SLinus Torvalds n->nud_state = NUD_NOARP; 3641da177e4SLinus Torvalds else 3651da177e4SLinus Torvalds n->nud_state = NUD_NONE; 366d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is stray\n", n); 3671da177e4SLinus Torvalds } 3681da177e4SLinus Torvalds write_unlock(&n->lock); 3694f494554SThomas Graf neigh_cleanup_and_release(n); 3701da177e4SLinus Torvalds } 3711da177e4SLinus Torvalds } 37249636bb1SHerbert Xu } 3731da177e4SLinus Torvalds 37449636bb1SHerbert Xu void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev) 37549636bb1SHerbert Xu { 37649636bb1SHerbert Xu write_lock_bh(&tbl->lock); 377859bd2efSDavid Ahern neigh_flush_dev(tbl, dev, false); 37849636bb1SHerbert Xu write_unlock_bh(&tbl->lock); 37949636bb1SHerbert Xu } 3800a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_changeaddr); 38149636bb1SHerbert Xu 382859bd2efSDavid Ahern static int __neigh_ifdown(struct neigh_table *tbl, struct net_device *dev, 383859bd2efSDavid Ahern bool skip_perm) 38449636bb1SHerbert Xu { 38549636bb1SHerbert Xu write_lock_bh(&tbl->lock); 386859bd2efSDavid Ahern neigh_flush_dev(tbl, dev, skip_perm); 38753b76cdfSWolfgang Bumiller pneigh_ifdown_and_unlock(tbl, dev); 3881da177e4SLinus Torvalds 3891da177e4SLinus Torvalds del_timer_sync(&tbl->proxy_timer); 3901da177e4SLinus Torvalds pneigh_queue_purge(&tbl->proxy_queue); 3911da177e4SLinus Torvalds return 0; 3921da177e4SLinus Torvalds } 393859bd2efSDavid Ahern 394859bd2efSDavid Ahern int neigh_carrier_down(struct neigh_table *tbl, struct net_device *dev) 395859bd2efSDavid Ahern { 396859bd2efSDavid Ahern __neigh_ifdown(tbl, dev, true); 397859bd2efSDavid Ahern return 0; 398859bd2efSDavid Ahern } 399859bd2efSDavid Ahern EXPORT_SYMBOL(neigh_carrier_down); 400859bd2efSDavid Ahern 401859bd2efSDavid Ahern int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev) 402859bd2efSDavid Ahern { 403859bd2efSDavid Ahern __neigh_ifdown(tbl, dev, false); 404859bd2efSDavid Ahern return 0; 405859bd2efSDavid Ahern } 4060a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_ifdown); 4071da177e4SLinus Torvalds 40858956317SDavid Ahern static struct neighbour *neigh_alloc(struct neigh_table *tbl, 40958956317SDavid Ahern struct net_device *dev, 4102c611ad9SRoopa Prabhu u32 flags, bool exempt_from_gc) 4111da177e4SLinus Torvalds { 4121da177e4SLinus Torvalds struct neighbour *n = NULL; 4131da177e4SLinus Torvalds unsigned long now = jiffies; 4141da177e4SLinus Torvalds int entries; 4151da177e4SLinus Torvalds 416e997f8a2SDavid Ahern if (exempt_from_gc) 41758956317SDavid Ahern goto do_alloc; 41858956317SDavid Ahern 41958956317SDavid Ahern entries = atomic_inc_return(&tbl->gc_entries) - 1; 4201da177e4SLinus Torvalds if (entries >= tbl->gc_thresh3 || 4211da177e4SLinus Torvalds (entries >= tbl->gc_thresh2 && 4221da177e4SLinus Torvalds time_after(now, tbl->last_flush + 5 * HZ))) { 4231da177e4SLinus Torvalds if (!neigh_forced_gc(tbl) && 424fb811395SRick Jones entries >= tbl->gc_thresh3) { 425fb811395SRick Jones net_info_ratelimited("%s: neighbor table overflow!\n", 426fb811395SRick Jones tbl->id); 427fb811395SRick Jones NEIGH_CACHE_STAT_INC(tbl, table_fulls); 4281da177e4SLinus Torvalds goto out_entries; 4291da177e4SLinus Torvalds } 430fb811395SRick Jones } 4311da177e4SLinus Torvalds 43258956317SDavid Ahern do_alloc: 43308433effSYOSHIFUJI Hideaki / 吉藤英明 n = kzalloc(tbl->entry_size + dev->neigh_priv_len, GFP_ATOMIC); 4341da177e4SLinus Torvalds if (!n) 4351da177e4SLinus Torvalds goto out_entries; 4361da177e4SLinus Torvalds 437c9ab4d85SEric Dumazet __skb_queue_head_init(&n->arp_queue); 4381da177e4SLinus Torvalds rwlock_init(&n->lock); 4390ed8ddf4SEric Dumazet seqlock_init(&n->ha_lock); 4401da177e4SLinus Torvalds n->updated = n->used = now; 4411da177e4SLinus Torvalds n->nud_state = NUD_NONE; 4421da177e4SLinus Torvalds n->output = neigh_blackhole; 443e4400bbfSDaniel Borkmann n->flags = flags; 444f6b72b62SDavid S. Miller seqlock_init(&n->hh.hh_lock); 4451da177e4SLinus Torvalds n->parms = neigh_parms_clone(&tbl->parms); 446e99e88a9SKees Cook timer_setup(&n->timer, neigh_timer_handler, 0); 4471da177e4SLinus Torvalds 4481da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, allocs); 4491da177e4SLinus Torvalds n->tbl = tbl; 4509f237430SReshetova, Elena refcount_set(&n->refcnt, 1); 4511da177e4SLinus Torvalds n->dead = 1; 45258956317SDavid Ahern INIT_LIST_HEAD(&n->gc_list); 4537482e384SDaniel Borkmann INIT_LIST_HEAD(&n->managed_list); 45458956317SDavid Ahern 45558956317SDavid Ahern atomic_inc(&tbl->entries); 4561da177e4SLinus Torvalds out: 4571da177e4SLinus Torvalds return n; 4581da177e4SLinus Torvalds 4591da177e4SLinus Torvalds out_entries: 460e997f8a2SDavid Ahern if (!exempt_from_gc) 46158956317SDavid Ahern atomic_dec(&tbl->gc_entries); 4621da177e4SLinus Torvalds goto out; 4631da177e4SLinus Torvalds } 4641da177e4SLinus Torvalds 4652c2aba6cSDavid S. Miller static void neigh_get_hash_rnd(u32 *x) 4662c2aba6cSDavid S. Miller { 467b3d0f789SJason A. Donenfeld *x = get_random_u32() | 1; 4682c2aba6cSDavid S. Miller } 4692c2aba6cSDavid S. Miller 470cd089336SDavid S. Miller static struct neigh_hash_table *neigh_hash_alloc(unsigned int shift) 4711da177e4SLinus Torvalds { 472cd089336SDavid S. Miller size_t size = (1 << shift) * sizeof(struct neighbour *); 473d6bf7817SEric Dumazet struct neigh_hash_table *ret; 4746193d2beSEric Dumazet struct neighbour __rcu **buckets; 4752c2aba6cSDavid S. Miller int i; 4761da177e4SLinus Torvalds 477d6bf7817SEric Dumazet ret = kmalloc(sizeof(*ret), GFP_ATOMIC); 478d6bf7817SEric Dumazet if (!ret) 479d6bf7817SEric Dumazet return NULL; 48085704cb8SKonstantin Khlebnikov if (size <= PAGE_SIZE) { 481d6bf7817SEric Dumazet buckets = kzalloc(size, GFP_ATOMIC); 48285704cb8SKonstantin Khlebnikov } else { 4836193d2beSEric Dumazet buckets = (struct neighbour __rcu **) 484d6bf7817SEric Dumazet __get_free_pages(GFP_ATOMIC | __GFP_ZERO, 485d6bf7817SEric Dumazet get_order(size)); 48601b833abSKonstantin Khlebnikov kmemleak_alloc(buckets, size, 1, GFP_ATOMIC); 48785704cb8SKonstantin Khlebnikov } 488d6bf7817SEric Dumazet if (!buckets) { 489d6bf7817SEric Dumazet kfree(ret); 490d6bf7817SEric Dumazet return NULL; 4911da177e4SLinus Torvalds } 4926193d2beSEric Dumazet ret->hash_buckets = buckets; 493cd089336SDavid S. Miller ret->hash_shift = shift; 4942c2aba6cSDavid S. Miller for (i = 0; i < NEIGH_NUM_HASH_RND; i++) 4952c2aba6cSDavid S. Miller neigh_get_hash_rnd(&ret->hash_rnd[i]); 4961da177e4SLinus Torvalds return ret; 4971da177e4SLinus Torvalds } 4981da177e4SLinus Torvalds 499d6bf7817SEric Dumazet static void neigh_hash_free_rcu(struct rcu_head *head) 5001da177e4SLinus Torvalds { 501d6bf7817SEric Dumazet struct neigh_hash_table *nht = container_of(head, 502d6bf7817SEric Dumazet struct neigh_hash_table, 503d6bf7817SEric Dumazet rcu); 504cd089336SDavid S. Miller size_t size = (1 << nht->hash_shift) * sizeof(struct neighbour *); 5056193d2beSEric Dumazet struct neighbour __rcu **buckets = nht->hash_buckets; 5061da177e4SLinus Torvalds 50785704cb8SKonstantin Khlebnikov if (size <= PAGE_SIZE) { 508d6bf7817SEric Dumazet kfree(buckets); 50985704cb8SKonstantin Khlebnikov } else { 51085704cb8SKonstantin Khlebnikov kmemleak_free(buckets); 511d6bf7817SEric Dumazet free_pages((unsigned long)buckets, get_order(size)); 51285704cb8SKonstantin Khlebnikov } 513d6bf7817SEric Dumazet kfree(nht); 5141da177e4SLinus Torvalds } 5151da177e4SLinus Torvalds 516d6bf7817SEric Dumazet static struct neigh_hash_table *neigh_hash_grow(struct neigh_table *tbl, 517cd089336SDavid S. Miller unsigned long new_shift) 5181da177e4SLinus Torvalds { 519d6bf7817SEric Dumazet unsigned int i, hash; 520d6bf7817SEric Dumazet struct neigh_hash_table *new_nht, *old_nht; 5211da177e4SLinus Torvalds 5221da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, hash_grows); 5231da177e4SLinus Torvalds 524d6bf7817SEric Dumazet old_nht = rcu_dereference_protected(tbl->nht, 525d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 526cd089336SDavid S. Miller new_nht = neigh_hash_alloc(new_shift); 527d6bf7817SEric Dumazet if (!new_nht) 528d6bf7817SEric Dumazet return old_nht; 5291da177e4SLinus Torvalds 530cd089336SDavid S. Miller for (i = 0; i < (1 << old_nht->hash_shift); i++) { 5311da177e4SLinus Torvalds struct neighbour *n, *next; 5321da177e4SLinus Torvalds 533767e97e1SEric Dumazet for (n = rcu_dereference_protected(old_nht->hash_buckets[i], 534767e97e1SEric Dumazet lockdep_is_held(&tbl->lock)); 535d6bf7817SEric Dumazet n != NULL; 536d6bf7817SEric Dumazet n = next) { 537d6bf7817SEric Dumazet hash = tbl->hash(n->primary_key, n->dev, 538d6bf7817SEric Dumazet new_nht->hash_rnd); 5391da177e4SLinus Torvalds 540cd089336SDavid S. Miller hash >>= (32 - new_nht->hash_shift); 541767e97e1SEric Dumazet next = rcu_dereference_protected(n->next, 542767e97e1SEric Dumazet lockdep_is_held(&tbl->lock)); 5431da177e4SLinus Torvalds 544767e97e1SEric Dumazet rcu_assign_pointer(n->next, 545767e97e1SEric Dumazet rcu_dereference_protected( 546767e97e1SEric Dumazet new_nht->hash_buckets[hash], 547767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))); 548767e97e1SEric Dumazet rcu_assign_pointer(new_nht->hash_buckets[hash], n); 5491da177e4SLinus Torvalds } 5501da177e4SLinus Torvalds } 5511da177e4SLinus Torvalds 552d6bf7817SEric Dumazet rcu_assign_pointer(tbl->nht, new_nht); 553d6bf7817SEric Dumazet call_rcu(&old_nht->rcu, neigh_hash_free_rcu); 554d6bf7817SEric Dumazet return new_nht; 5551da177e4SLinus Torvalds } 5561da177e4SLinus Torvalds 5571da177e4SLinus Torvalds struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey, 5581da177e4SLinus Torvalds struct net_device *dev) 5591da177e4SLinus Torvalds { 5601da177e4SLinus Torvalds struct neighbour *n; 5611da177e4SLinus Torvalds 5621da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, lookups); 5631da177e4SLinus Torvalds 564d6bf7817SEric Dumazet rcu_read_lock_bh(); 56560395a20SEric W. Biederman n = __neigh_lookup_noref(tbl, pkey, dev); 56660395a20SEric W. Biederman if (n) { 5679f237430SReshetova, Elena if (!refcount_inc_not_zero(&n->refcnt)) 568767e97e1SEric Dumazet n = NULL; 5691da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, hits); 5701da177e4SLinus Torvalds } 571767e97e1SEric Dumazet 572d6bf7817SEric Dumazet rcu_read_unlock_bh(); 5731da177e4SLinus Torvalds return n; 5741da177e4SLinus Torvalds } 5750a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_lookup); 5761da177e4SLinus Torvalds 577426b5303SEric W. Biederman struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net, 578426b5303SEric W. Biederman const void *pkey) 5791da177e4SLinus Torvalds { 5801da177e4SLinus Torvalds struct neighbour *n; 58101ccdf12SAlexey Dobriyan unsigned int key_len = tbl->key_len; 582bc4bf5f3SPavel Emelyanov u32 hash_val; 583d6bf7817SEric Dumazet struct neigh_hash_table *nht; 5841da177e4SLinus Torvalds 5851da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, lookups); 5861da177e4SLinus Torvalds 587d6bf7817SEric Dumazet rcu_read_lock_bh(); 588d6bf7817SEric Dumazet nht = rcu_dereference_bh(tbl->nht); 589cd089336SDavid S. Miller hash_val = tbl->hash(pkey, NULL, nht->hash_rnd) >> (32 - nht->hash_shift); 590767e97e1SEric Dumazet 591767e97e1SEric Dumazet for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]); 592767e97e1SEric Dumazet n != NULL; 593767e97e1SEric Dumazet n = rcu_dereference_bh(n->next)) { 594426b5303SEric W. Biederman if (!memcmp(n->primary_key, pkey, key_len) && 595878628fbSYOSHIFUJI Hideaki net_eq(dev_net(n->dev), net)) { 5969f237430SReshetova, Elena if (!refcount_inc_not_zero(&n->refcnt)) 597767e97e1SEric Dumazet n = NULL; 5981da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, hits); 5991da177e4SLinus Torvalds break; 6001da177e4SLinus Torvalds } 6011da177e4SLinus Torvalds } 602767e97e1SEric Dumazet 603d6bf7817SEric Dumazet rcu_read_unlock_bh(); 6041da177e4SLinus Torvalds return n; 6051da177e4SLinus Torvalds } 6060a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_lookup_nodev); 6071da177e4SLinus Torvalds 608e4400bbfSDaniel Borkmann static struct neighbour * 609e4400bbfSDaniel Borkmann ___neigh_create(struct neigh_table *tbl, const void *pkey, 6102c611ad9SRoopa Prabhu struct net_device *dev, u32 flags, 611e997f8a2SDavid Ahern bool exempt_from_gc, bool want_ref) 6121da177e4SLinus Torvalds { 613e4400bbfSDaniel Borkmann u32 hash_val, key_len = tbl->key_len; 614e4400bbfSDaniel Borkmann struct neighbour *n1, *rc, *n; 615d6bf7817SEric Dumazet struct neigh_hash_table *nht; 616e4400bbfSDaniel Borkmann int error; 6171da177e4SLinus Torvalds 618e4400bbfSDaniel Borkmann n = neigh_alloc(tbl, dev, flags, exempt_from_gc); 619fc651001SDavid Ahern trace_neigh_create(tbl, dev, pkey, n, exempt_from_gc); 6201da177e4SLinus Torvalds if (!n) { 6211da177e4SLinus Torvalds rc = ERR_PTR(-ENOBUFS); 6221da177e4SLinus Torvalds goto out; 6231da177e4SLinus Torvalds } 6241da177e4SLinus Torvalds 6251da177e4SLinus Torvalds memcpy(n->primary_key, pkey, key_len); 6261da177e4SLinus Torvalds n->dev = dev; 627*d62607c3SJakub Kicinski netdev_hold(dev, &n->dev_tracker, GFP_ATOMIC); 6281da177e4SLinus Torvalds 6291da177e4SLinus Torvalds /* Protocol specific setup. */ 6301da177e4SLinus Torvalds if (tbl->constructor && (error = tbl->constructor(n)) < 0) { 6311da177e4SLinus Torvalds rc = ERR_PTR(error); 6321da177e4SLinus Torvalds goto out_neigh_release; 6331da177e4SLinus Torvalds } 6341da177e4SLinus Torvalds 635da6a8fa0SDavid Miller if (dev->netdev_ops->ndo_neigh_construct) { 636503eebc2SJiri Pirko error = dev->netdev_ops->ndo_neigh_construct(dev, n); 637da6a8fa0SDavid Miller if (error < 0) { 638da6a8fa0SDavid Miller rc = ERR_PTR(error); 639da6a8fa0SDavid Miller goto out_neigh_release; 640da6a8fa0SDavid Miller } 641da6a8fa0SDavid Miller } 642da6a8fa0SDavid Miller 643447f2191SDavid S. Miller /* Device specific setup. */ 644447f2191SDavid S. Miller if (n->parms->neigh_setup && 645447f2191SDavid S. Miller (error = n->parms->neigh_setup(n)) < 0) { 646447f2191SDavid S. Miller rc = ERR_PTR(error); 647447f2191SDavid S. Miller goto out_neigh_release; 648447f2191SDavid S. Miller } 649447f2191SDavid S. Miller 6501f9248e5SJiri Pirko n->confirmed = jiffies - (NEIGH_VAR(n->parms, BASE_REACHABLE_TIME) << 1); 6511da177e4SLinus Torvalds 6521da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 653d6bf7817SEric Dumazet nht = rcu_dereference_protected(tbl->nht, 654d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 6551da177e4SLinus Torvalds 656cd089336SDavid S. Miller if (atomic_read(&tbl->entries) > (1 << nht->hash_shift)) 657cd089336SDavid S. Miller nht = neigh_hash_grow(tbl, nht->hash_shift + 1); 6581da177e4SLinus Torvalds 659096b9854SJim Westfall hash_val = tbl->hash(n->primary_key, dev, nht->hash_rnd) >> (32 - nht->hash_shift); 6601da177e4SLinus Torvalds 6611da177e4SLinus Torvalds if (n->parms->dead) { 6621da177e4SLinus Torvalds rc = ERR_PTR(-EINVAL); 6631da177e4SLinus Torvalds goto out_tbl_unlock; 6641da177e4SLinus Torvalds } 6651da177e4SLinus Torvalds 666767e97e1SEric Dumazet for (n1 = rcu_dereference_protected(nht->hash_buckets[hash_val], 667767e97e1SEric Dumazet lockdep_is_held(&tbl->lock)); 668767e97e1SEric Dumazet n1 != NULL; 669767e97e1SEric Dumazet n1 = rcu_dereference_protected(n1->next, 670767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))) { 671096b9854SJim Westfall if (dev == n1->dev && !memcmp(n1->primary_key, n->primary_key, key_len)) { 672a263b309SDavid S. Miller if (want_ref) 6731da177e4SLinus Torvalds neigh_hold(n1); 6741da177e4SLinus Torvalds rc = n1; 6751da177e4SLinus Torvalds goto out_tbl_unlock; 6761da177e4SLinus Torvalds } 6771da177e4SLinus Torvalds } 6781da177e4SLinus Torvalds 6791da177e4SLinus Torvalds n->dead = 0; 680e997f8a2SDavid Ahern if (!exempt_from_gc) 6818cc196d6SDavid Ahern list_add_tail(&n->gc_list, &n->tbl->gc_list); 6827482e384SDaniel Borkmann if (n->flags & NTF_MANAGED) 6837482e384SDaniel Borkmann list_add_tail(&n->managed_list, &n->tbl->managed_list); 684a263b309SDavid S. Miller if (want_ref) 6851da177e4SLinus Torvalds neigh_hold(n); 686767e97e1SEric Dumazet rcu_assign_pointer(n->next, 687767e97e1SEric Dumazet rcu_dereference_protected(nht->hash_buckets[hash_val], 688767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))); 689767e97e1SEric Dumazet rcu_assign_pointer(nht->hash_buckets[hash_val], n); 6901da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 691d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is created\n", n); 6921da177e4SLinus Torvalds rc = n; 6931da177e4SLinus Torvalds out: 6941da177e4SLinus Torvalds return rc; 6951da177e4SLinus Torvalds out_tbl_unlock: 6961da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 6971da177e4SLinus Torvalds out_neigh_release: 69864c6f4bbSDavid Ahern if (!exempt_from_gc) 69964c6f4bbSDavid Ahern atomic_dec(&tbl->gc_entries); 7001da177e4SLinus Torvalds neigh_release(n); 7011da177e4SLinus Torvalds goto out; 7021da177e4SLinus Torvalds } 70358956317SDavid Ahern 70458956317SDavid Ahern struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey, 70558956317SDavid Ahern struct net_device *dev, bool want_ref) 70658956317SDavid Ahern { 707e4400bbfSDaniel Borkmann return ___neigh_create(tbl, pkey, dev, 0, false, want_ref); 70858956317SDavid Ahern } 709a263b309SDavid S. Miller EXPORT_SYMBOL(__neigh_create); 7101da177e4SLinus Torvalds 71101ccdf12SAlexey Dobriyan static u32 pneigh_hash(const void *pkey, unsigned int key_len) 712fa86d322SPavel Emelyanov { 713fa86d322SPavel Emelyanov u32 hash_val = *(u32 *)(pkey + key_len - 4); 714fa86d322SPavel Emelyanov hash_val ^= (hash_val >> 16); 715fa86d322SPavel Emelyanov hash_val ^= hash_val >> 8; 716fa86d322SPavel Emelyanov hash_val ^= hash_val >> 4; 717fa86d322SPavel Emelyanov hash_val &= PNEIGH_HASHMASK; 718be01d655SYOSHIFUJI Hideaki return hash_val; 719fa86d322SPavel Emelyanov } 720fa86d322SPavel Emelyanov 721be01d655SYOSHIFUJI Hideaki static struct pneigh_entry *__pneigh_lookup_1(struct pneigh_entry *n, 722be01d655SYOSHIFUJI Hideaki struct net *net, 723be01d655SYOSHIFUJI Hideaki const void *pkey, 72401ccdf12SAlexey Dobriyan unsigned int key_len, 725be01d655SYOSHIFUJI Hideaki struct net_device *dev) 726be01d655SYOSHIFUJI Hideaki { 727be01d655SYOSHIFUJI Hideaki while (n) { 728be01d655SYOSHIFUJI Hideaki if (!memcmp(n->key, pkey, key_len) && 729be01d655SYOSHIFUJI Hideaki net_eq(pneigh_net(n), net) && 730be01d655SYOSHIFUJI Hideaki (n->dev == dev || !n->dev)) 731fa86d322SPavel Emelyanov return n; 732be01d655SYOSHIFUJI Hideaki n = n->next; 733be01d655SYOSHIFUJI Hideaki } 734be01d655SYOSHIFUJI Hideaki return NULL; 735be01d655SYOSHIFUJI Hideaki } 736be01d655SYOSHIFUJI Hideaki 737be01d655SYOSHIFUJI Hideaki struct pneigh_entry *__pneigh_lookup(struct neigh_table *tbl, 738be01d655SYOSHIFUJI Hideaki struct net *net, const void *pkey, struct net_device *dev) 739be01d655SYOSHIFUJI Hideaki { 74001ccdf12SAlexey Dobriyan unsigned int key_len = tbl->key_len; 741be01d655SYOSHIFUJI Hideaki u32 hash_val = pneigh_hash(pkey, key_len); 742be01d655SYOSHIFUJI Hideaki 743be01d655SYOSHIFUJI Hideaki return __pneigh_lookup_1(tbl->phash_buckets[hash_val], 744be01d655SYOSHIFUJI Hideaki net, pkey, key_len, dev); 745fa86d322SPavel Emelyanov } 7460a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL_GPL(__pneigh_lookup); 747fa86d322SPavel Emelyanov 748426b5303SEric W. Biederman struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, 749426b5303SEric W. Biederman struct net *net, const void *pkey, 7501da177e4SLinus Torvalds struct net_device *dev, int creat) 7511da177e4SLinus Torvalds { 7521da177e4SLinus Torvalds struct pneigh_entry *n; 75301ccdf12SAlexey Dobriyan unsigned int key_len = tbl->key_len; 754be01d655SYOSHIFUJI Hideaki u32 hash_val = pneigh_hash(pkey, key_len); 7551da177e4SLinus Torvalds 7561da177e4SLinus Torvalds read_lock_bh(&tbl->lock); 757be01d655SYOSHIFUJI Hideaki n = __pneigh_lookup_1(tbl->phash_buckets[hash_val], 758be01d655SYOSHIFUJI Hideaki net, pkey, key_len, dev); 759be01d655SYOSHIFUJI Hideaki read_unlock_bh(&tbl->lock); 7601da177e4SLinus Torvalds 761be01d655SYOSHIFUJI Hideaki if (n || !creat) 7621da177e4SLinus Torvalds goto out; 7631da177e4SLinus Torvalds 7644ae28944SPavel Emelyanov ASSERT_RTNL(); 7654ae28944SPavel Emelyanov 766e195e9b5SEric Dumazet n = kzalloc(sizeof(*n) + key_len, GFP_KERNEL); 7671da177e4SLinus Torvalds if (!n) 7681da177e4SLinus Torvalds goto out; 7691da177e4SLinus Torvalds 770efd7ef1cSEric W. Biederman write_pnet(&n->net, net); 7711da177e4SLinus Torvalds memcpy(n->key, pkey, key_len); 7721da177e4SLinus Torvalds n->dev = dev; 773*d62607c3SJakub Kicinski netdev_hold(dev, &n->dev_tracker, GFP_KERNEL); 7741da177e4SLinus Torvalds 7751da177e4SLinus Torvalds if (tbl->pconstructor && tbl->pconstructor(n)) { 776*d62607c3SJakub Kicinski netdev_put(dev, &n->dev_tracker); 7771da177e4SLinus Torvalds kfree(n); 7781da177e4SLinus Torvalds n = NULL; 7791da177e4SLinus Torvalds goto out; 7801da177e4SLinus Torvalds } 7811da177e4SLinus Torvalds 7821da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 7831da177e4SLinus Torvalds n->next = tbl->phash_buckets[hash_val]; 7841da177e4SLinus Torvalds tbl->phash_buckets[hash_val] = n; 7851da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 7861da177e4SLinus Torvalds out: 7871da177e4SLinus Torvalds return n; 7881da177e4SLinus Torvalds } 7890a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(pneigh_lookup); 7901da177e4SLinus Torvalds 7911da177e4SLinus Torvalds 792426b5303SEric W. Biederman int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey, 7931da177e4SLinus Torvalds struct net_device *dev) 7941da177e4SLinus Torvalds { 7951da177e4SLinus Torvalds struct pneigh_entry *n, **np; 79601ccdf12SAlexey Dobriyan unsigned int key_len = tbl->key_len; 797be01d655SYOSHIFUJI Hideaki u32 hash_val = pneigh_hash(pkey, key_len); 7981da177e4SLinus Torvalds 7991da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 8001da177e4SLinus Torvalds for (np = &tbl->phash_buckets[hash_val]; (n = *np) != NULL; 8011da177e4SLinus Torvalds np = &n->next) { 802426b5303SEric W. Biederman if (!memcmp(n->key, pkey, key_len) && n->dev == dev && 803878628fbSYOSHIFUJI Hideaki net_eq(pneigh_net(n), net)) { 8041da177e4SLinus Torvalds *np = n->next; 8051da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 8061da177e4SLinus Torvalds if (tbl->pdestructor) 8071da177e4SLinus Torvalds tbl->pdestructor(n); 808*d62607c3SJakub Kicinski netdev_put(n->dev, &n->dev_tracker); 8091da177e4SLinus Torvalds kfree(n); 8101da177e4SLinus Torvalds return 0; 8111da177e4SLinus Torvalds } 8121da177e4SLinus Torvalds } 8131da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 8141da177e4SLinus Torvalds return -ENOENT; 8151da177e4SLinus Torvalds } 8161da177e4SLinus Torvalds 81753b76cdfSWolfgang Bumiller static int pneigh_ifdown_and_unlock(struct neigh_table *tbl, 81853b76cdfSWolfgang Bumiller struct net_device *dev) 8191da177e4SLinus Torvalds { 82053b76cdfSWolfgang Bumiller struct pneigh_entry *n, **np, *freelist = NULL; 8211da177e4SLinus Torvalds u32 h; 8221da177e4SLinus Torvalds 8231da177e4SLinus Torvalds for (h = 0; h <= PNEIGH_HASHMASK; h++) { 8241da177e4SLinus Torvalds np = &tbl->phash_buckets[h]; 8251da177e4SLinus Torvalds while ((n = *np) != NULL) { 8261da177e4SLinus Torvalds if (!dev || n->dev == dev) { 8271da177e4SLinus Torvalds *np = n->next; 82853b76cdfSWolfgang Bumiller n->next = freelist; 82953b76cdfSWolfgang Bumiller freelist = n; 83053b76cdfSWolfgang Bumiller continue; 83153b76cdfSWolfgang Bumiller } 83253b76cdfSWolfgang Bumiller np = &n->next; 83353b76cdfSWolfgang Bumiller } 83453b76cdfSWolfgang Bumiller } 83553b76cdfSWolfgang Bumiller write_unlock_bh(&tbl->lock); 83653b76cdfSWolfgang Bumiller while ((n = freelist)) { 83753b76cdfSWolfgang Bumiller freelist = n->next; 83853b76cdfSWolfgang Bumiller n->next = NULL; 8391da177e4SLinus Torvalds if (tbl->pdestructor) 8401da177e4SLinus Torvalds tbl->pdestructor(n); 841*d62607c3SJakub Kicinski netdev_put(n->dev, &n->dev_tracker); 8421da177e4SLinus Torvalds kfree(n); 8431da177e4SLinus Torvalds } 8441da177e4SLinus Torvalds return -ENOENT; 8451da177e4SLinus Torvalds } 8461da177e4SLinus Torvalds 84706f0511dSDenis V. Lunev static void neigh_parms_destroy(struct neigh_parms *parms); 84806f0511dSDenis V. Lunev 84906f0511dSDenis V. Lunev static inline void neigh_parms_put(struct neigh_parms *parms) 85006f0511dSDenis V. Lunev { 8516343944bSReshetova, Elena if (refcount_dec_and_test(&parms->refcnt)) 85206f0511dSDenis V. Lunev neigh_parms_destroy(parms); 85306f0511dSDenis V. Lunev } 8541da177e4SLinus Torvalds 8551da177e4SLinus Torvalds /* 8561da177e4SLinus Torvalds * neighbour must already be out of the table; 8571da177e4SLinus Torvalds * 8581da177e4SLinus Torvalds */ 8591da177e4SLinus Torvalds void neigh_destroy(struct neighbour *neigh) 8601da177e4SLinus Torvalds { 861da6a8fa0SDavid Miller struct net_device *dev = neigh->dev; 862da6a8fa0SDavid Miller 8631da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(neigh->tbl, destroys); 8641da177e4SLinus Torvalds 8651da177e4SLinus Torvalds if (!neigh->dead) { 866e005d193SJoe Perches pr_warn("Destroying alive neighbour %p\n", neigh); 8671da177e4SLinus Torvalds dump_stack(); 8681da177e4SLinus Torvalds return; 8691da177e4SLinus Torvalds } 8701da177e4SLinus Torvalds 8711da177e4SLinus Torvalds if (neigh_del_timer(neigh)) 872e005d193SJoe Perches pr_warn("Impossible event\n"); 8731da177e4SLinus Torvalds 874c9ab4d85SEric Dumazet write_lock_bh(&neigh->lock); 875c9ab4d85SEric Dumazet __skb_queue_purge(&neigh->arp_queue); 876c9ab4d85SEric Dumazet write_unlock_bh(&neigh->lock); 8778b5c171bSEric Dumazet neigh->arp_queue_len_bytes = 0; 8781da177e4SLinus Torvalds 879447f2191SDavid S. Miller if (dev->netdev_ops->ndo_neigh_destroy) 880503eebc2SJiri Pirko dev->netdev_ops->ndo_neigh_destroy(dev, neigh); 881447f2191SDavid S. Miller 882*d62607c3SJakub Kicinski netdev_put(dev, &neigh->dev_tracker); 8831da177e4SLinus Torvalds neigh_parms_put(neigh->parms); 8841da177e4SLinus Torvalds 885d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is destroyed\n", neigh); 8861da177e4SLinus Torvalds 8871da177e4SLinus Torvalds atomic_dec(&neigh->tbl->entries); 8885b8b0060SDavid Miller kfree_rcu(neigh, rcu); 8891da177e4SLinus Torvalds } 8900a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_destroy); 8911da177e4SLinus Torvalds 8921da177e4SLinus Torvalds /* Neighbour state is suspicious; 8931da177e4SLinus Torvalds disable fast path. 8941da177e4SLinus Torvalds 8951da177e4SLinus Torvalds Called with write_locked neigh. 8961da177e4SLinus Torvalds */ 8971da177e4SLinus Torvalds static void neigh_suspect(struct neighbour *neigh) 8981da177e4SLinus Torvalds { 899d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is suspected\n", neigh); 9001da177e4SLinus Torvalds 9011da177e4SLinus Torvalds neigh->output = neigh->ops->output; 9021da177e4SLinus Torvalds } 9031da177e4SLinus Torvalds 9041da177e4SLinus Torvalds /* Neighbour state is OK; 9051da177e4SLinus Torvalds enable fast path. 9061da177e4SLinus Torvalds 9071da177e4SLinus Torvalds Called with write_locked neigh. 9081da177e4SLinus Torvalds */ 9091da177e4SLinus Torvalds static void neigh_connect(struct neighbour *neigh) 9101da177e4SLinus Torvalds { 911d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is connected\n", neigh); 9121da177e4SLinus Torvalds 9131da177e4SLinus Torvalds neigh->output = neigh->ops->connected_output; 9141da177e4SLinus Torvalds } 9151da177e4SLinus Torvalds 916e4c4e448SEric Dumazet static void neigh_periodic_work(struct work_struct *work) 9171da177e4SLinus Torvalds { 918e4c4e448SEric Dumazet struct neigh_table *tbl = container_of(work, struct neigh_table, gc_work.work); 919767e97e1SEric Dumazet struct neighbour *n; 920767e97e1SEric Dumazet struct neighbour __rcu **np; 921e4c4e448SEric Dumazet unsigned int i; 922d6bf7817SEric Dumazet struct neigh_hash_table *nht; 9231da177e4SLinus Torvalds 9241da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, periodic_gc_runs); 9251da177e4SLinus Torvalds 926e4c4e448SEric Dumazet write_lock_bh(&tbl->lock); 927d6bf7817SEric Dumazet nht = rcu_dereference_protected(tbl->nht, 928d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 9291da177e4SLinus Torvalds 9301da177e4SLinus Torvalds /* 9311da177e4SLinus Torvalds * periodically recompute ReachableTime from random function 9321da177e4SLinus Torvalds */ 9331da177e4SLinus Torvalds 934e4c4e448SEric Dumazet if (time_after(jiffies, tbl->last_rand + 300 * HZ)) { 9351da177e4SLinus Torvalds struct neigh_parms *p; 936e4c4e448SEric Dumazet tbl->last_rand = jiffies; 93775fbfd33SNicolas Dichtel list_for_each_entry(p, &tbl->parms_list, list) 9381da177e4SLinus Torvalds p->reachable_time = 9391f9248e5SJiri Pirko neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); 9401da177e4SLinus Torvalds } 9411da177e4SLinus Torvalds 942feff9ab2SDuan Jiong if (atomic_read(&tbl->entries) < tbl->gc_thresh1) 943feff9ab2SDuan Jiong goto out; 944feff9ab2SDuan Jiong 945cd089336SDavid S. Miller for (i = 0 ; i < (1 << nht->hash_shift); i++) { 946d6bf7817SEric Dumazet np = &nht->hash_buckets[i]; 9471da177e4SLinus Torvalds 948767e97e1SEric Dumazet while ((n = rcu_dereference_protected(*np, 949767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))) != NULL) { 9501da177e4SLinus Torvalds unsigned int state; 9511da177e4SLinus Torvalds 9521da177e4SLinus Torvalds write_lock(&n->lock); 9531da177e4SLinus Torvalds 9541da177e4SLinus Torvalds state = n->nud_state; 9559ce33e46SRoopa Prabhu if ((state & (NUD_PERMANENT | NUD_IN_TIMER)) || 9569ce33e46SRoopa Prabhu (n->flags & NTF_EXT_LEARNED)) { 9571da177e4SLinus Torvalds write_unlock(&n->lock); 9581da177e4SLinus Torvalds goto next_elt; 9591da177e4SLinus Torvalds } 9601da177e4SLinus Torvalds 9611da177e4SLinus Torvalds if (time_before(n->used, n->confirmed)) 9621da177e4SLinus Torvalds n->used = n->confirmed; 9631da177e4SLinus Torvalds 9649f237430SReshetova, Elena if (refcount_read(&n->refcnt) == 1 && 9651da177e4SLinus Torvalds (state == NUD_FAILED || 9661f9248e5SJiri Pirko time_after(jiffies, n->used + NEIGH_VAR(n->parms, GC_STALETIME)))) { 9671da177e4SLinus Torvalds *np = n->next; 96858956317SDavid Ahern neigh_mark_dead(n); 9691da177e4SLinus Torvalds write_unlock(&n->lock); 9704f494554SThomas Graf neigh_cleanup_and_release(n); 9711da177e4SLinus Torvalds continue; 9721da177e4SLinus Torvalds } 9731da177e4SLinus Torvalds write_unlock(&n->lock); 9741da177e4SLinus Torvalds 9751da177e4SLinus Torvalds next_elt: 9761da177e4SLinus Torvalds np = &n->next; 9771da177e4SLinus Torvalds } 978e4c4e448SEric Dumazet /* 979e4c4e448SEric Dumazet * It's fine to release lock here, even if hash table 980e4c4e448SEric Dumazet * grows while we are preempted. 981e4c4e448SEric Dumazet */ 982e4c4e448SEric Dumazet write_unlock_bh(&tbl->lock); 983e4c4e448SEric Dumazet cond_resched(); 984e4c4e448SEric Dumazet write_lock_bh(&tbl->lock); 98584338a6cSMichel Machado nht = rcu_dereference_protected(tbl->nht, 98684338a6cSMichel Machado lockdep_is_held(&tbl->lock)); 987e4c4e448SEric Dumazet } 9882724680bSYOSHIFUJI Hideaki / 吉藤英明 out: 9891f9248e5SJiri Pirko /* Cycle through all hash buckets every BASE_REACHABLE_TIME/2 ticks. 9901f9248e5SJiri Pirko * ARP entry timeouts range from 1/2 BASE_REACHABLE_TIME to 3/2 9911f9248e5SJiri Pirko * BASE_REACHABLE_TIME. 9921da177e4SLinus Torvalds */ 993f618002bSviresh kumar queue_delayed_work(system_power_efficient_wq, &tbl->gc_work, 9941f9248e5SJiri Pirko NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME) >> 1); 995e4c4e448SEric Dumazet write_unlock_bh(&tbl->lock); 9961da177e4SLinus Torvalds } 9971da177e4SLinus Torvalds 9981da177e4SLinus Torvalds static __inline__ int neigh_max_probes(struct neighbour *n) 9991da177e4SLinus Torvalds { 10001da177e4SLinus Torvalds struct neigh_parms *p = n->parms; 10018da86466SYOSHIFUJI Hideaki/吉藤英明 return NEIGH_VAR(p, UCAST_PROBES) + NEIGH_VAR(p, APP_PROBES) + 10028da86466SYOSHIFUJI Hideaki/吉藤英明 (n->nud_state & NUD_PROBE ? NEIGH_VAR(p, MCAST_REPROBES) : 10038da86466SYOSHIFUJI Hideaki/吉藤英明 NEIGH_VAR(p, MCAST_PROBES)); 10041da177e4SLinus Torvalds } 10051da177e4SLinus Torvalds 10065ef12d98STimo Teras static void neigh_invalidate(struct neighbour *neigh) 10070a141509SEric Dumazet __releases(neigh->lock) 10080a141509SEric Dumazet __acquires(neigh->lock) 10095ef12d98STimo Teras { 10105ef12d98STimo Teras struct sk_buff *skb; 10115ef12d98STimo Teras 10125ef12d98STimo Teras NEIGH_CACHE_STAT_INC(neigh->tbl, res_failed); 1013d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is failed\n", neigh); 10145ef12d98STimo Teras neigh->updated = jiffies; 10155ef12d98STimo Teras 10165ef12d98STimo Teras /* It is very thin place. report_unreachable is very complicated 10175ef12d98STimo Teras routine. Particularly, it can hit the same neighbour entry! 10185ef12d98STimo Teras 10195ef12d98STimo Teras So that, we try to be accurate and avoid dead loop. --ANK 10205ef12d98STimo Teras */ 10215ef12d98STimo Teras while (neigh->nud_state == NUD_FAILED && 10225ef12d98STimo Teras (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) { 10235ef12d98STimo Teras write_unlock(&neigh->lock); 10245ef12d98STimo Teras neigh->ops->error_report(neigh, skb); 10255ef12d98STimo Teras write_lock(&neigh->lock); 10265ef12d98STimo Teras } 1027c9ab4d85SEric Dumazet __skb_queue_purge(&neigh->arp_queue); 10288b5c171bSEric Dumazet neigh->arp_queue_len_bytes = 0; 10295ef12d98STimo Teras } 10305ef12d98STimo Teras 1031cd28ca0aSEric Dumazet static void neigh_probe(struct neighbour *neigh) 1032cd28ca0aSEric Dumazet __releases(neigh->lock) 1033cd28ca0aSEric Dumazet { 10344ed377e3SHannes Frederic Sowa struct sk_buff *skb = skb_peek_tail(&neigh->arp_queue); 1035cd28ca0aSEric Dumazet /* keep skb alive even if arp_queue overflows */ 1036cd28ca0aSEric Dumazet if (skb) 103719125c1aSMartin Zhang skb = skb_clone(skb, GFP_ATOMIC); 1038cd28ca0aSEric Dumazet write_unlock(&neigh->lock); 103948481c8fSEric Dumazet if (neigh->ops->solicit) 1040cd28ca0aSEric Dumazet neigh->ops->solicit(neigh, skb); 1041cd28ca0aSEric Dumazet atomic_inc(&neigh->probes); 104287fff3caSYang Wei consume_skb(skb); 1043cd28ca0aSEric Dumazet } 1044cd28ca0aSEric Dumazet 10451da177e4SLinus Torvalds /* Called when a timer expires for a neighbour entry. */ 10461da177e4SLinus Torvalds 1047e99e88a9SKees Cook static void neigh_timer_handler(struct timer_list *t) 10481da177e4SLinus Torvalds { 10491da177e4SLinus Torvalds unsigned long now, next; 1050e99e88a9SKees Cook struct neighbour *neigh = from_timer(neigh, t, timer); 105195c96174SEric Dumazet unsigned int state; 10521da177e4SLinus Torvalds int notify = 0; 10531da177e4SLinus Torvalds 10541da177e4SLinus Torvalds write_lock(&neigh->lock); 10551da177e4SLinus Torvalds 10561da177e4SLinus Torvalds state = neigh->nud_state; 10571da177e4SLinus Torvalds now = jiffies; 10581da177e4SLinus Torvalds next = now + HZ; 10591da177e4SLinus Torvalds 1060045f7b3bSDavid S. Miller if (!(state & NUD_IN_TIMER)) 10611da177e4SLinus Torvalds goto out; 10621da177e4SLinus Torvalds 10631da177e4SLinus Torvalds if (state & NUD_REACHABLE) { 10641da177e4SLinus Torvalds if (time_before_eq(now, 10651da177e4SLinus Torvalds neigh->confirmed + neigh->parms->reachable_time)) { 1066d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is still alive\n", neigh); 10671da177e4SLinus Torvalds next = neigh->confirmed + neigh->parms->reachable_time; 10681da177e4SLinus Torvalds } else if (time_before_eq(now, 10691f9248e5SJiri Pirko neigh->used + 10701f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) { 1071d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is delayed\n", neigh); 10721da177e4SLinus Torvalds neigh->nud_state = NUD_DELAY; 1073955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 10741da177e4SLinus Torvalds neigh_suspect(neigh); 10751f9248e5SJiri Pirko next = now + NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME); 10761da177e4SLinus Torvalds } else { 1077d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is suspected\n", neigh); 10781da177e4SLinus Torvalds neigh->nud_state = NUD_STALE; 1079955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 10801da177e4SLinus Torvalds neigh_suspect(neigh); 10818d71740cSTom Tucker notify = 1; 10821da177e4SLinus Torvalds } 10831da177e4SLinus Torvalds } else if (state & NUD_DELAY) { 10841da177e4SLinus Torvalds if (time_before_eq(now, 10851f9248e5SJiri Pirko neigh->confirmed + 10861f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) { 1087d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is now reachable\n", neigh); 10881da177e4SLinus Torvalds neigh->nud_state = NUD_REACHABLE; 1089955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 10901da177e4SLinus Torvalds neigh_connect(neigh); 10918d71740cSTom Tucker notify = 1; 10921da177e4SLinus Torvalds next = neigh->confirmed + neigh->parms->reachable_time; 10931da177e4SLinus Torvalds } else { 1094d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is probed\n", neigh); 10951da177e4SLinus Torvalds neigh->nud_state = NUD_PROBE; 1096955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 10971da177e4SLinus Torvalds atomic_set(&neigh->probes, 0); 1098765c9c63SErik Kline notify = 1; 109919e16d22SHangbin Liu next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), 110019e16d22SHangbin Liu HZ/100); 11011da177e4SLinus Torvalds } 11021da177e4SLinus Torvalds } else { 11031da177e4SLinus Torvalds /* NUD_PROBE|NUD_INCOMPLETE */ 110419e16d22SHangbin Liu next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), HZ/100); 11051da177e4SLinus Torvalds } 11061da177e4SLinus Torvalds 11071da177e4SLinus Torvalds if ((neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) && 11081da177e4SLinus Torvalds atomic_read(&neigh->probes) >= neigh_max_probes(neigh)) { 11091da177e4SLinus Torvalds neigh->nud_state = NUD_FAILED; 11101da177e4SLinus Torvalds notify = 1; 11115ef12d98STimo Teras neigh_invalidate(neigh); 11125e2c21dcSDuan Jiong goto out; 11131da177e4SLinus Torvalds } 11141da177e4SLinus Torvalds 11151da177e4SLinus Torvalds if (neigh->nud_state & NUD_IN_TIMER) { 111696d10d5bSHangbin Liu if (time_before(next, jiffies + HZ/100)) 111796d10d5bSHangbin Liu next = jiffies + HZ/100; 11186fb9974fSHerbert Xu if (!mod_timer(&neigh->timer, next)) 11196fb9974fSHerbert Xu neigh_hold(neigh); 11201da177e4SLinus Torvalds } 11211da177e4SLinus Torvalds if (neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) { 1122cd28ca0aSEric Dumazet neigh_probe(neigh); 11239ff56607SDavid S. Miller } else { 11241da177e4SLinus Torvalds out: 11251da177e4SLinus Torvalds write_unlock(&neigh->lock); 11269ff56607SDavid S. Miller } 11271da177e4SLinus Torvalds 1128d961db35SThomas Graf if (notify) 11297b8f7a40SRoopa Prabhu neigh_update_notify(neigh, 0); 1130d961db35SThomas Graf 113156dd18a4SRoopa Prabhu trace_neigh_timer_handler(neigh, 0); 113256dd18a4SRoopa Prabhu 11331da177e4SLinus Torvalds neigh_release(neigh); 11341da177e4SLinus Torvalds } 11351da177e4SLinus Torvalds 11364a81f6daSDaniel Borkmann int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb, 11374a81f6daSDaniel Borkmann const bool immediate_ok) 11381da177e4SLinus Torvalds { 11391da177e4SLinus Torvalds int rc; 1140cd28ca0aSEric Dumazet bool immediate_probe = false; 11411da177e4SLinus Torvalds 11421da177e4SLinus Torvalds write_lock_bh(&neigh->lock); 11431da177e4SLinus Torvalds 11441da177e4SLinus Torvalds rc = 0; 11451da177e4SLinus Torvalds if (neigh->nud_state & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE)) 11461da177e4SLinus Torvalds goto out_unlock_bh; 11472c51a97fSJulian Anastasov if (neigh->dead) 11482c51a97fSJulian Anastasov goto out_dead; 11491da177e4SLinus Torvalds 11501da177e4SLinus Torvalds if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) { 11511f9248e5SJiri Pirko if (NEIGH_VAR(neigh->parms, MCAST_PROBES) + 11521f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, APP_PROBES)) { 1153cd28ca0aSEric Dumazet unsigned long next, now = jiffies; 1154cd28ca0aSEric Dumazet 11551f9248e5SJiri Pirko atomic_set(&neigh->probes, 11561f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, UCAST_PROBES)); 1157071c3798SLorenzo Bianconi neigh_del_timer(neigh); 11581da177e4SLinus Torvalds neigh->nud_state = NUD_INCOMPLETE; 1159cd28ca0aSEric Dumazet neigh->updated = now; 11604a81f6daSDaniel Borkmann if (!immediate_ok) { 11614a81f6daSDaniel Borkmann next = now + 1; 11624a81f6daSDaniel Borkmann } else { 1163cd28ca0aSEric Dumazet immediate_probe = true; 11644a81f6daSDaniel Borkmann next = now + max(NEIGH_VAR(neigh->parms, 11654a81f6daSDaniel Borkmann RETRANS_TIME), 11664a81f6daSDaniel Borkmann HZ / 100); 11674a81f6daSDaniel Borkmann } 11684a81f6daSDaniel Borkmann neigh_add_timer(neigh, next); 11691da177e4SLinus Torvalds } else { 11701da177e4SLinus Torvalds neigh->nud_state = NUD_FAILED; 1171955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 11721da177e4SLinus Torvalds write_unlock_bh(&neigh->lock); 11731da177e4SLinus Torvalds 1174a5736eddSMenglong Dong kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_FAILED); 11751da177e4SLinus Torvalds return 1; 11761da177e4SLinus Torvalds } 11771da177e4SLinus Torvalds } else if (neigh->nud_state & NUD_STALE) { 1178d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is delayed\n", neigh); 1179071c3798SLorenzo Bianconi neigh_del_timer(neigh); 11801da177e4SLinus Torvalds neigh->nud_state = NUD_DELAY; 1181955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 11821f9248e5SJiri Pirko neigh_add_timer(neigh, jiffies + 11831f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME)); 11841da177e4SLinus Torvalds } 11851da177e4SLinus Torvalds 11861da177e4SLinus Torvalds if (neigh->nud_state == NUD_INCOMPLETE) { 11871da177e4SLinus Torvalds if (skb) { 11888b5c171bSEric Dumazet while (neigh->arp_queue_len_bytes + skb->truesize > 11891f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, QUEUE_LEN_BYTES)) { 11901da177e4SLinus Torvalds struct sk_buff *buff; 11918b5c171bSEric Dumazet 1192f72051b0SDavid S. Miller buff = __skb_dequeue(&neigh->arp_queue); 11938b5c171bSEric Dumazet if (!buff) 11948b5c171bSEric Dumazet break; 11958b5c171bSEric Dumazet neigh->arp_queue_len_bytes -= buff->truesize; 1196a5736eddSMenglong Dong kfree_skb_reason(buff, SKB_DROP_REASON_NEIGH_QUEUEFULL); 11979a6d276eSNeil Horman NEIGH_CACHE_STAT_INC(neigh->tbl, unres_discards); 11981da177e4SLinus Torvalds } 1199a4731138SEric Dumazet skb_dst_force(skb); 12001da177e4SLinus Torvalds __skb_queue_tail(&neigh->arp_queue, skb); 12018b5c171bSEric Dumazet neigh->arp_queue_len_bytes += skb->truesize; 12021da177e4SLinus Torvalds } 12031da177e4SLinus Torvalds rc = 1; 12041da177e4SLinus Torvalds } 12051da177e4SLinus Torvalds out_unlock_bh: 1206cd28ca0aSEric Dumazet if (immediate_probe) 1207cd28ca0aSEric Dumazet neigh_probe(neigh); 1208cd28ca0aSEric Dumazet else 1209cd28ca0aSEric Dumazet write_unlock(&neigh->lock); 1210cd28ca0aSEric Dumazet local_bh_enable(); 121156dd18a4SRoopa Prabhu trace_neigh_event_send_done(neigh, rc); 12121da177e4SLinus Torvalds return rc; 12132c51a97fSJulian Anastasov 12142c51a97fSJulian Anastasov out_dead: 12152c51a97fSJulian Anastasov if (neigh->nud_state & NUD_STALE) 12162c51a97fSJulian Anastasov goto out_unlock_bh; 12172c51a97fSJulian Anastasov write_unlock_bh(&neigh->lock); 1218a5736eddSMenglong Dong kfree_skb_reason(skb, SKB_DROP_REASON_NEIGH_DEAD); 121956dd18a4SRoopa Prabhu trace_neigh_event_send_dead(neigh, 1); 12202c51a97fSJulian Anastasov return 1; 12211da177e4SLinus Torvalds } 12220a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(__neigh_event_send); 12231da177e4SLinus Torvalds 1224f6b72b62SDavid S. Miller static void neigh_update_hhs(struct neighbour *neigh) 12251da177e4SLinus Torvalds { 12261da177e4SLinus Torvalds struct hh_cache *hh; 12273b04dddeSStephen Hemminger void (*update)(struct hh_cache*, const struct net_device*, const unsigned char *) 122891a72a70SDoug Kehn = NULL; 122991a72a70SDoug Kehn 123091a72a70SDoug Kehn if (neigh->dev->header_ops) 123191a72a70SDoug Kehn update = neigh->dev->header_ops->cache_update; 12321da177e4SLinus Torvalds 12331da177e4SLinus Torvalds if (update) { 1234f6b72b62SDavid S. Miller hh = &neigh->hh; 1235c305c6aeSEric Dumazet if (READ_ONCE(hh->hh_len)) { 12363644f0ceSStephen Hemminger write_seqlock_bh(&hh->hh_lock); 12371da177e4SLinus Torvalds update(hh, neigh->dev, neigh->ha); 12383644f0ceSStephen Hemminger write_sequnlock_bh(&hh->hh_lock); 12391da177e4SLinus Torvalds } 12401da177e4SLinus Torvalds } 12411da177e4SLinus Torvalds } 12421da177e4SLinus Torvalds 12431da177e4SLinus Torvalds /* Generic update routine. 12441da177e4SLinus Torvalds -- lladdr is new lladdr or NULL, if it is not supplied. 12451da177e4SLinus Torvalds -- new is new state. 12461da177e4SLinus Torvalds -- flags 12471da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE allows to override existing lladdr, 12481da177e4SLinus Torvalds if it is different. 12491da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE will suspect existing "connected" 12501da177e4SLinus Torvalds lladdr instead of overriding it 12511da177e4SLinus Torvalds if it is different. 12521da177e4SLinus Torvalds NEIGH_UPDATE_F_ADMIN means that the change is administrative. 12533dc20f47SDaniel Borkmann NEIGH_UPDATE_F_USE means that the entry is user triggered. 12547482e384SDaniel Borkmann NEIGH_UPDATE_F_MANAGED means that the entry will be auto-refreshed. 12551da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE_ISROUTER allows to override existing 12561da177e4SLinus Torvalds NTF_ROUTER flag. 12571da177e4SLinus Torvalds NEIGH_UPDATE_F_ISROUTER indicates if the neighbour is known as 12581da177e4SLinus Torvalds a router. 12591da177e4SLinus Torvalds 12601da177e4SLinus Torvalds Caller MUST hold reference count on the entry. 12611da177e4SLinus Torvalds */ 12627a35a50dSDavid Ahern static int __neigh_update(struct neighbour *neigh, const u8 *lladdr, 12637a35a50dSDavid Ahern u8 new, u32 flags, u32 nlmsg_pid, 12647a35a50dSDavid Ahern struct netlink_ext_ack *extack) 12651da177e4SLinus Torvalds { 12667482e384SDaniel Borkmann bool gc_update = false, managed_update = false; 12671da177e4SLinus Torvalds int update_isrouter = 0; 12687482e384SDaniel Borkmann struct net_device *dev; 12697482e384SDaniel Borkmann int err, notify = 0; 12707482e384SDaniel Borkmann u8 old; 12711da177e4SLinus Torvalds 127256dd18a4SRoopa Prabhu trace_neigh_update(neigh, lladdr, new, flags, nlmsg_pid); 127356dd18a4SRoopa Prabhu 12741da177e4SLinus Torvalds write_lock_bh(&neigh->lock); 12751da177e4SLinus Torvalds 12761da177e4SLinus Torvalds dev = neigh->dev; 12771da177e4SLinus Torvalds old = neigh->nud_state; 12781da177e4SLinus Torvalds err = -EPERM; 12791da177e4SLinus Torvalds 1280eb4e8facSChinmay Agarwal if (neigh->dead) { 1281eb4e8facSChinmay Agarwal NL_SET_ERR_MSG(extack, "Neighbor entry is now dead"); 1282eb4e8facSChinmay Agarwal new = old; 1283eb4e8facSChinmay Agarwal goto out; 1284eb4e8facSChinmay Agarwal } 12851da177e4SLinus Torvalds if (!(flags & NEIGH_UPDATE_F_ADMIN) && 12861da177e4SLinus Torvalds (old & (NUD_NOARP | NUD_PERMANENT))) 12871da177e4SLinus Torvalds goto out; 12881da177e4SLinus Torvalds 12897482e384SDaniel Borkmann neigh_update_flags(neigh, flags, ¬ify, &gc_update, &managed_update); 12907482e384SDaniel Borkmann if (flags & (NEIGH_UPDATE_F_USE | NEIGH_UPDATE_F_MANAGED)) { 12913dc20f47SDaniel Borkmann new = old & ~NUD_PERMANENT; 12923dc20f47SDaniel Borkmann neigh->nud_state = new; 12933dc20f47SDaniel Borkmann err = 0; 12943dc20f47SDaniel Borkmann goto out; 12953dc20f47SDaniel Borkmann } 12969ce33e46SRoopa Prabhu 12971da177e4SLinus Torvalds if (!(new & NUD_VALID)) { 12981da177e4SLinus Torvalds neigh_del_timer(neigh); 12991da177e4SLinus Torvalds if (old & NUD_CONNECTED) 13001da177e4SLinus Torvalds neigh_suspect(neigh); 13019c29a2f5SDavid Ahern neigh->nud_state = new; 13021da177e4SLinus Torvalds err = 0; 13031da177e4SLinus Torvalds notify = old & NUD_VALID; 1304d2fb4fb8SRoopa Prabhu if ((old & (NUD_INCOMPLETE | NUD_PROBE)) && 13055ef12d98STimo Teras (new & NUD_FAILED)) { 13065ef12d98STimo Teras neigh_invalidate(neigh); 13075ef12d98STimo Teras notify = 1; 13085ef12d98STimo Teras } 13091da177e4SLinus Torvalds goto out; 13101da177e4SLinus Torvalds } 13111da177e4SLinus Torvalds 13121da177e4SLinus Torvalds /* Compare new lladdr with cached one */ 13131da177e4SLinus Torvalds if (!dev->addr_len) { 13141da177e4SLinus Torvalds /* First case: device needs no address. */ 13151da177e4SLinus Torvalds lladdr = neigh->ha; 13161da177e4SLinus Torvalds } else if (lladdr) { 13171da177e4SLinus Torvalds /* The second case: if something is already cached 13181da177e4SLinus Torvalds and a new address is proposed: 13191da177e4SLinus Torvalds - compare new & old 13201da177e4SLinus Torvalds - if they are different, check override flag 13211da177e4SLinus Torvalds */ 13221da177e4SLinus Torvalds if ((old & NUD_VALID) && 13231da177e4SLinus Torvalds !memcmp(lladdr, neigh->ha, dev->addr_len)) 13241da177e4SLinus Torvalds lladdr = neigh->ha; 13251da177e4SLinus Torvalds } else { 13261da177e4SLinus Torvalds /* No address is supplied; if we know something, 13271da177e4SLinus Torvalds use it, otherwise discard the request. 13281da177e4SLinus Torvalds */ 13291da177e4SLinus Torvalds err = -EINVAL; 13307a35a50dSDavid Ahern if (!(old & NUD_VALID)) { 13317a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "No link layer address given"); 13321da177e4SLinus Torvalds goto out; 13337a35a50dSDavid Ahern } 13341da177e4SLinus Torvalds lladdr = neigh->ha; 13351da177e4SLinus Torvalds } 13361da177e4SLinus Torvalds 1337f0e0d044SVasily Khoruzhick /* Update confirmed timestamp for neighbour entry after we 1338f0e0d044SVasily Khoruzhick * received ARP packet even if it doesn't change IP to MAC binding. 1339f0e0d044SVasily Khoruzhick */ 1340f0e0d044SVasily Khoruzhick if (new & NUD_CONNECTED) 1341f0e0d044SVasily Khoruzhick neigh->confirmed = jiffies; 1342f0e0d044SVasily Khoruzhick 13431da177e4SLinus Torvalds /* If entry was valid and address is not changed, 13441da177e4SLinus Torvalds do not change entry state, if new one is STALE. 13451da177e4SLinus Torvalds */ 13461da177e4SLinus Torvalds err = 0; 13471da177e4SLinus Torvalds update_isrouter = flags & NEIGH_UPDATE_F_OVERRIDE_ISROUTER; 13481da177e4SLinus Torvalds if (old & NUD_VALID) { 13491da177e4SLinus Torvalds if (lladdr != neigh->ha && !(flags & NEIGH_UPDATE_F_OVERRIDE)) { 13501da177e4SLinus Torvalds update_isrouter = 0; 13511da177e4SLinus Torvalds if ((flags & NEIGH_UPDATE_F_WEAK_OVERRIDE) && 13521da177e4SLinus Torvalds (old & NUD_CONNECTED)) { 13531da177e4SLinus Torvalds lladdr = neigh->ha; 13541da177e4SLinus Torvalds new = NUD_STALE; 13551da177e4SLinus Torvalds } else 13561da177e4SLinus Torvalds goto out; 13571da177e4SLinus Torvalds } else { 13580e7bbcc1SJulian Anastasov if (lladdr == neigh->ha && new == NUD_STALE && 13590e7bbcc1SJulian Anastasov !(flags & NEIGH_UPDATE_F_ADMIN)) 13601da177e4SLinus Torvalds new = old; 13611da177e4SLinus Torvalds } 13621da177e4SLinus Torvalds } 13631da177e4SLinus Torvalds 1364f0e0d044SVasily Khoruzhick /* Update timestamp only once we know we will make a change to the 136577d71233SIhar Hrachyshka * neighbour entry. Otherwise we risk to move the locktime window with 136677d71233SIhar Hrachyshka * noop updates and ignore relevant ARP updates. 136777d71233SIhar Hrachyshka */ 1368f0e0d044SVasily Khoruzhick if (new != old || lladdr != neigh->ha) 136977d71233SIhar Hrachyshka neigh->updated = jiffies; 137077d71233SIhar Hrachyshka 13711da177e4SLinus Torvalds if (new != old) { 13721da177e4SLinus Torvalds neigh_del_timer(neigh); 1373765c9c63SErik Kline if (new & NUD_PROBE) 1374765c9c63SErik Kline atomic_set(&neigh->probes, 0); 1375a43d8994SPavel Emelyanov if (new & NUD_IN_TIMER) 1376667347f1SDavid S. Miller neigh_add_timer(neigh, (jiffies + 13771da177e4SLinus Torvalds ((new & NUD_REACHABLE) ? 1378667347f1SDavid S. Miller neigh->parms->reachable_time : 1379667347f1SDavid S. Miller 0))); 13809c29a2f5SDavid Ahern neigh->nud_state = new; 138153385d2dSBob Gilligan notify = 1; 13821da177e4SLinus Torvalds } 13831da177e4SLinus Torvalds 13841da177e4SLinus Torvalds if (lladdr != neigh->ha) { 13850ed8ddf4SEric Dumazet write_seqlock(&neigh->ha_lock); 13861da177e4SLinus Torvalds memcpy(&neigh->ha, lladdr, dev->addr_len); 13870ed8ddf4SEric Dumazet write_sequnlock(&neigh->ha_lock); 13881da177e4SLinus Torvalds neigh_update_hhs(neigh); 13891da177e4SLinus Torvalds if (!(new & NUD_CONNECTED)) 13901da177e4SLinus Torvalds neigh->confirmed = jiffies - 13911f9248e5SJiri Pirko (NEIGH_VAR(neigh->parms, BASE_REACHABLE_TIME) << 1); 13921da177e4SLinus Torvalds notify = 1; 13931da177e4SLinus Torvalds } 13941da177e4SLinus Torvalds if (new == old) 13951da177e4SLinus Torvalds goto out; 13961da177e4SLinus Torvalds if (new & NUD_CONNECTED) 13971da177e4SLinus Torvalds neigh_connect(neigh); 13981da177e4SLinus Torvalds else 13991da177e4SLinus Torvalds neigh_suspect(neigh); 14001da177e4SLinus Torvalds if (!(old & NUD_VALID)) { 14011da177e4SLinus Torvalds struct sk_buff *skb; 14021da177e4SLinus Torvalds 14031da177e4SLinus Torvalds /* Again: avoid dead loop if something went wrong */ 14041da177e4SLinus Torvalds 14051da177e4SLinus Torvalds while (neigh->nud_state & NUD_VALID && 14061da177e4SLinus Torvalds (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) { 140769cce1d1SDavid S. Miller struct dst_entry *dst = skb_dst(skb); 140869cce1d1SDavid S. Miller struct neighbour *n2, *n1 = neigh; 14091da177e4SLinus Torvalds write_unlock_bh(&neigh->lock); 1410e049f288Sroy.qing.li@gmail.com 1411e049f288Sroy.qing.li@gmail.com rcu_read_lock(); 141213a43d94SDavid S. Miller 141313a43d94SDavid S. Miller /* Why not just use 'neigh' as-is? The problem is that 141413a43d94SDavid S. Miller * things such as shaper, eql, and sch_teql can end up 141513a43d94SDavid S. Miller * using alternative, different, neigh objects to output 141613a43d94SDavid S. Miller * the packet in the output path. So what we need to do 141713a43d94SDavid S. Miller * here is re-lookup the top-level neigh in the path so 141813a43d94SDavid S. Miller * we can reinject the packet there. 141913a43d94SDavid S. Miller */ 142013a43d94SDavid S. Miller n2 = NULL; 1421d47ec7a0STong Zhu if (dst && dst->obsolete != DST_OBSOLETE_DEAD) { 142213a43d94SDavid S. Miller n2 = dst_neigh_lookup_skb(dst, skb); 142313a43d94SDavid S. Miller if (n2) 142469cce1d1SDavid S. Miller n1 = n2; 142513a43d94SDavid S. Miller } 14268f40b161SDavid S. Miller n1->output(n1, skb); 142713a43d94SDavid S. Miller if (n2) 142813a43d94SDavid S. Miller neigh_release(n2); 1429e049f288Sroy.qing.li@gmail.com rcu_read_unlock(); 1430e049f288Sroy.qing.li@gmail.com 14311da177e4SLinus Torvalds write_lock_bh(&neigh->lock); 14321da177e4SLinus Torvalds } 1433c9ab4d85SEric Dumazet __skb_queue_purge(&neigh->arp_queue); 14348b5c171bSEric Dumazet neigh->arp_queue_len_bytes = 0; 14351da177e4SLinus Torvalds } 14361da177e4SLinus Torvalds out: 1437fc6e8073SRoopa Prabhu if (update_isrouter) 1438fc6e8073SRoopa Prabhu neigh_update_is_router(neigh, flags, ¬ify); 14391da177e4SLinus Torvalds write_unlock_bh(&neigh->lock); 14407482e384SDaniel Borkmann if (((new ^ old) & NUD_PERMANENT) || gc_update) 14419c29a2f5SDavid Ahern neigh_update_gc_list(neigh); 14427482e384SDaniel Borkmann if (managed_update) 14437482e384SDaniel Borkmann neigh_update_managed_list(neigh); 14448d71740cSTom Tucker if (notify) 14457b8f7a40SRoopa Prabhu neigh_update_notify(neigh, nlmsg_pid); 144656dd18a4SRoopa Prabhu trace_neigh_update_done(neigh, err); 14471da177e4SLinus Torvalds return err; 14481da177e4SLinus Torvalds } 14497a35a50dSDavid Ahern 14507a35a50dSDavid Ahern int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, 14517a35a50dSDavid Ahern u32 flags, u32 nlmsg_pid) 14527a35a50dSDavid Ahern { 14537a35a50dSDavid Ahern return __neigh_update(neigh, lladdr, new, flags, nlmsg_pid, NULL); 14547a35a50dSDavid Ahern } 14550a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_update); 14561da177e4SLinus Torvalds 14577e980569SJiri Benc /* Update the neigh to listen temporarily for probe responses, even if it is 14587e980569SJiri Benc * in a NUD_FAILED state. The caller has to hold neigh->lock for writing. 14597e980569SJiri Benc */ 14607e980569SJiri Benc void __neigh_set_probe_once(struct neighbour *neigh) 14617e980569SJiri Benc { 14622c51a97fSJulian Anastasov if (neigh->dead) 14632c51a97fSJulian Anastasov return; 14647e980569SJiri Benc neigh->updated = jiffies; 14657e980569SJiri Benc if (!(neigh->nud_state & NUD_FAILED)) 14667e980569SJiri Benc return; 14672176d5d4SDuan Jiong neigh->nud_state = NUD_INCOMPLETE; 14682176d5d4SDuan Jiong atomic_set(&neigh->probes, neigh_max_probes(neigh)); 14697e980569SJiri Benc neigh_add_timer(neigh, 147019e16d22SHangbin Liu jiffies + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), 147119e16d22SHangbin Liu HZ/100)); 14727e980569SJiri Benc } 14737e980569SJiri Benc EXPORT_SYMBOL(__neigh_set_probe_once); 14747e980569SJiri Benc 14751da177e4SLinus Torvalds struct neighbour *neigh_event_ns(struct neigh_table *tbl, 14761da177e4SLinus Torvalds u8 *lladdr, void *saddr, 14771da177e4SLinus Torvalds struct net_device *dev) 14781da177e4SLinus Torvalds { 14791da177e4SLinus Torvalds struct neighbour *neigh = __neigh_lookup(tbl, saddr, dev, 14801da177e4SLinus Torvalds lladdr || !dev->addr_len); 14811da177e4SLinus Torvalds if (neigh) 14821da177e4SLinus Torvalds neigh_update(neigh, lladdr, NUD_STALE, 14837b8f7a40SRoopa Prabhu NEIGH_UPDATE_F_OVERRIDE, 0); 14841da177e4SLinus Torvalds return neigh; 14851da177e4SLinus Torvalds } 14860a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_event_ns); 14871da177e4SLinus Torvalds 148834d101ddSEric Dumazet /* called with read_lock_bh(&n->lock); */ 1489bdf53c58SEric W. Biederman static void neigh_hh_init(struct neighbour *n) 14901da177e4SLinus Torvalds { 1491bdf53c58SEric W. Biederman struct net_device *dev = n->dev; 1492bdf53c58SEric W. Biederman __be16 prot = n->tbl->protocol; 1493f6b72b62SDavid S. Miller struct hh_cache *hh = &n->hh; 14940ed8ddf4SEric Dumazet 14950ed8ddf4SEric Dumazet write_lock_bh(&n->lock); 149634d101ddSEric Dumazet 1497f6b72b62SDavid S. Miller /* Only one thread can come in here and initialize the 1498f6b72b62SDavid S. Miller * hh_cache entry. 1499f6b72b62SDavid S. Miller */ 1500b23b5455SDavid S. Miller if (!hh->hh_len) 1501b23b5455SDavid S. Miller dev->header_ops->cache(n, hh, prot); 1502f6b72b62SDavid S. Miller 15030ed8ddf4SEric Dumazet write_unlock_bh(&n->lock); 15041da177e4SLinus Torvalds } 15051da177e4SLinus Torvalds 15061da177e4SLinus Torvalds /* Slow and careful. */ 15071da177e4SLinus Torvalds 15088f40b161SDavid S. Miller int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb) 15091da177e4SLinus Torvalds { 15101da177e4SLinus Torvalds int rc = 0; 15111da177e4SLinus Torvalds 15121da177e4SLinus Torvalds if (!neigh_event_send(neigh, skb)) { 15131da177e4SLinus Torvalds int err; 15141da177e4SLinus Torvalds struct net_device *dev = neigh->dev; 15150ed8ddf4SEric Dumazet unsigned int seq; 151634d101ddSEric Dumazet 1517c305c6aeSEric Dumazet if (dev->header_ops->cache && !READ_ONCE(neigh->hh.hh_len)) 1518bdf53c58SEric W. Biederman neigh_hh_init(neigh); 151934d101ddSEric Dumazet 15200ed8ddf4SEric Dumazet do { 1521e1f16503Sramesh.nagappa@gmail.com __skb_pull(skb, skb_network_offset(skb)); 15220ed8ddf4SEric Dumazet seq = read_seqbegin(&neigh->ha_lock); 15230c4e8581SStephen Hemminger err = dev_hard_header(skb, dev, ntohs(skb->protocol), 15241da177e4SLinus Torvalds neigh->ha, NULL, skb->len); 15250ed8ddf4SEric Dumazet } while (read_seqretry(&neigh->ha_lock, seq)); 152634d101ddSEric Dumazet 15271da177e4SLinus Torvalds if (err >= 0) 1528542d4d68SDavid S. Miller rc = dev_queue_xmit(skb); 15291da177e4SLinus Torvalds else 15301da177e4SLinus Torvalds goto out_kfree_skb; 15311da177e4SLinus Torvalds } 15321da177e4SLinus Torvalds out: 15331da177e4SLinus Torvalds return rc; 15341da177e4SLinus Torvalds out_kfree_skb: 15351da177e4SLinus Torvalds rc = -EINVAL; 15361da177e4SLinus Torvalds kfree_skb(skb); 15371da177e4SLinus Torvalds goto out; 15381da177e4SLinus Torvalds } 15390a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_resolve_output); 15401da177e4SLinus Torvalds 15411da177e4SLinus Torvalds /* As fast as possible without hh cache */ 15421da177e4SLinus Torvalds 15438f40b161SDavid S. Miller int neigh_connected_output(struct neighbour *neigh, struct sk_buff *skb) 15441da177e4SLinus Torvalds { 15451da177e4SLinus Torvalds struct net_device *dev = neigh->dev; 15460ed8ddf4SEric Dumazet unsigned int seq; 15478f40b161SDavid S. Miller int err; 15481da177e4SLinus Torvalds 15490ed8ddf4SEric Dumazet do { 1550e1f16503Sramesh.nagappa@gmail.com __skb_pull(skb, skb_network_offset(skb)); 15510ed8ddf4SEric Dumazet seq = read_seqbegin(&neigh->ha_lock); 15520c4e8581SStephen Hemminger err = dev_hard_header(skb, dev, ntohs(skb->protocol), 15531da177e4SLinus Torvalds neigh->ha, NULL, skb->len); 15540ed8ddf4SEric Dumazet } while (read_seqretry(&neigh->ha_lock, seq)); 15550ed8ddf4SEric Dumazet 15561da177e4SLinus Torvalds if (err >= 0) 1557542d4d68SDavid S. Miller err = dev_queue_xmit(skb); 15581da177e4SLinus Torvalds else { 15591da177e4SLinus Torvalds err = -EINVAL; 15601da177e4SLinus Torvalds kfree_skb(skb); 15611da177e4SLinus Torvalds } 15621da177e4SLinus Torvalds return err; 15631da177e4SLinus Torvalds } 15640a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_connected_output); 15651da177e4SLinus Torvalds 15668f40b161SDavid S. Miller int neigh_direct_output(struct neighbour *neigh, struct sk_buff *skb) 15678f40b161SDavid S. Miller { 15688f40b161SDavid S. Miller return dev_queue_xmit(skb); 15698f40b161SDavid S. Miller } 15708f40b161SDavid S. Miller EXPORT_SYMBOL(neigh_direct_output); 15718f40b161SDavid S. Miller 15727482e384SDaniel Borkmann static void neigh_managed_work(struct work_struct *work) 15737482e384SDaniel Borkmann { 15747482e384SDaniel Borkmann struct neigh_table *tbl = container_of(work, struct neigh_table, 15757482e384SDaniel Borkmann managed_work.work); 15767482e384SDaniel Borkmann struct neighbour *neigh; 15777482e384SDaniel Borkmann 15787482e384SDaniel Borkmann write_lock_bh(&tbl->lock); 15797482e384SDaniel Borkmann list_for_each_entry(neigh, &tbl->managed_list, managed_list) 15804a81f6daSDaniel Borkmann neigh_event_send_probe(neigh, NULL, false); 15817482e384SDaniel Borkmann queue_delayed_work(system_power_efficient_wq, &tbl->managed_work, 1582ed6cd6a1SDaniel Borkmann max(NEIGH_VAR(&tbl->parms, DELAY_PROBE_TIME), HZ)); 15837482e384SDaniel Borkmann write_unlock_bh(&tbl->lock); 15847482e384SDaniel Borkmann } 15857482e384SDaniel Borkmann 1586e99e88a9SKees Cook static void neigh_proxy_process(struct timer_list *t) 15871da177e4SLinus Torvalds { 1588e99e88a9SKees Cook struct neigh_table *tbl = from_timer(tbl, t, proxy_timer); 15891da177e4SLinus Torvalds long sched_next = 0; 15901da177e4SLinus Torvalds unsigned long now = jiffies; 1591f72051b0SDavid S. Miller struct sk_buff *skb, *n; 15921da177e4SLinus Torvalds 15931da177e4SLinus Torvalds spin_lock(&tbl->proxy_queue.lock); 15941da177e4SLinus Torvalds 1595f72051b0SDavid S. Miller skb_queue_walk_safe(&tbl->proxy_queue, skb, n) { 1596f72051b0SDavid S. Miller long tdif = NEIGH_CB(skb)->sched_next - now; 15971da177e4SLinus Torvalds 15981da177e4SLinus Torvalds if (tdif <= 0) { 1599f72051b0SDavid S. Miller struct net_device *dev = skb->dev; 160020e6074eSEric Dumazet 1601f72051b0SDavid S. Miller __skb_unlink(skb, &tbl->proxy_queue); 160220e6074eSEric Dumazet if (tbl->proxy_redo && netif_running(dev)) { 160320e6074eSEric Dumazet rcu_read_lock(); 1604f72051b0SDavid S. Miller tbl->proxy_redo(skb); 160520e6074eSEric Dumazet rcu_read_unlock(); 160620e6074eSEric Dumazet } else { 1607f72051b0SDavid S. Miller kfree_skb(skb); 160820e6074eSEric Dumazet } 16091da177e4SLinus Torvalds 16101da177e4SLinus Torvalds dev_put(dev); 16111da177e4SLinus Torvalds } else if (!sched_next || tdif < sched_next) 16121da177e4SLinus Torvalds sched_next = tdif; 16131da177e4SLinus Torvalds } 16141da177e4SLinus Torvalds del_timer(&tbl->proxy_timer); 16151da177e4SLinus Torvalds if (sched_next) 16161da177e4SLinus Torvalds mod_timer(&tbl->proxy_timer, jiffies + sched_next); 16171da177e4SLinus Torvalds spin_unlock(&tbl->proxy_queue.lock); 16181da177e4SLinus Torvalds } 16191da177e4SLinus Torvalds 16201da177e4SLinus Torvalds void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p, 16211da177e4SLinus Torvalds struct sk_buff *skb) 16221da177e4SLinus Torvalds { 1623a533b70aSweichenchen unsigned long sched_next = jiffies + 1624a533b70aSweichenchen prandom_u32_max(NEIGH_VAR(p, PROXY_DELAY)); 16251da177e4SLinus Torvalds 16261f9248e5SJiri Pirko if (tbl->proxy_queue.qlen > NEIGH_VAR(p, PROXY_QLEN)) { 16271da177e4SLinus Torvalds kfree_skb(skb); 16281da177e4SLinus Torvalds return; 16291da177e4SLinus Torvalds } 1630a61bbcf2SPatrick McHardy 1631a61bbcf2SPatrick McHardy NEIGH_CB(skb)->sched_next = sched_next; 1632a61bbcf2SPatrick McHardy NEIGH_CB(skb)->flags |= LOCALLY_ENQUEUED; 16331da177e4SLinus Torvalds 16341da177e4SLinus Torvalds spin_lock(&tbl->proxy_queue.lock); 16351da177e4SLinus Torvalds if (del_timer(&tbl->proxy_timer)) { 16361da177e4SLinus Torvalds if (time_before(tbl->proxy_timer.expires, sched_next)) 16371da177e4SLinus Torvalds sched_next = tbl->proxy_timer.expires; 16381da177e4SLinus Torvalds } 1639adf30907SEric Dumazet skb_dst_drop(skb); 16401da177e4SLinus Torvalds dev_hold(skb->dev); 16411da177e4SLinus Torvalds __skb_queue_tail(&tbl->proxy_queue, skb); 16421da177e4SLinus Torvalds mod_timer(&tbl->proxy_timer, sched_next); 16431da177e4SLinus Torvalds spin_unlock(&tbl->proxy_queue.lock); 16441da177e4SLinus Torvalds } 16450a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(pneigh_enqueue); 16461da177e4SLinus Torvalds 164797fd5bc7STobias Klauser static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl, 1648426b5303SEric W. Biederman struct net *net, int ifindex) 1649426b5303SEric W. Biederman { 1650426b5303SEric W. Biederman struct neigh_parms *p; 1651426b5303SEric W. Biederman 165275fbfd33SNicolas Dichtel list_for_each_entry(p, &tbl->parms_list, list) { 1653878628fbSYOSHIFUJI Hideaki if ((p->dev && p->dev->ifindex == ifindex && net_eq(neigh_parms_net(p), net)) || 1654170d6f99SGao feng (!p->dev && !ifindex && net_eq(net, &init_net))) 1655426b5303SEric W. Biederman return p; 1656426b5303SEric W. Biederman } 1657426b5303SEric W. Biederman 1658426b5303SEric W. Biederman return NULL; 1659426b5303SEric W. Biederman } 16601da177e4SLinus Torvalds 16611da177e4SLinus Torvalds struct neigh_parms *neigh_parms_alloc(struct net_device *dev, 16621da177e4SLinus Torvalds struct neigh_table *tbl) 16631da177e4SLinus Torvalds { 1664cf89d6b2SGao feng struct neigh_parms *p; 166500829823SStephen Hemminger struct net *net = dev_net(dev); 166600829823SStephen Hemminger const struct net_device_ops *ops = dev->netdev_ops; 16671da177e4SLinus Torvalds 1668cf89d6b2SGao feng p = kmemdup(&tbl->parms, sizeof(*p), GFP_KERNEL); 16691da177e4SLinus Torvalds if (p) { 16701da177e4SLinus Torvalds p->tbl = tbl; 16716343944bSReshetova, Elena refcount_set(&p->refcnt, 1); 16721da177e4SLinus Torvalds p->reachable_time = 16731f9248e5SJiri Pirko neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); 1674*d62607c3SJakub Kicinski netdev_hold(dev, &p->dev_tracker, GFP_KERNEL); 1675c7fb64dbSThomas Graf p->dev = dev; 1676efd7ef1cSEric W. Biederman write_pnet(&p->net, net); 16771da177e4SLinus Torvalds p->sysctl_table = NULL; 167863134803SVeaceslav Falico 167963134803SVeaceslav Falico if (ops->ndo_neigh_setup && ops->ndo_neigh_setup(dev, p)) { 1680*d62607c3SJakub Kicinski netdev_put(dev, &p->dev_tracker); 168163134803SVeaceslav Falico kfree(p); 168263134803SVeaceslav Falico return NULL; 168363134803SVeaceslav Falico } 168463134803SVeaceslav Falico 16851da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 168675fbfd33SNicolas Dichtel list_add(&p->list, &tbl->parms.list); 16871da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 16881d4c8c29SJiri Pirko 16891d4c8c29SJiri Pirko neigh_parms_data_state_cleanall(p); 16901da177e4SLinus Torvalds } 16911da177e4SLinus Torvalds return p; 16921da177e4SLinus Torvalds } 16930a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_parms_alloc); 16941da177e4SLinus Torvalds 16951da177e4SLinus Torvalds static void neigh_rcu_free_parms(struct rcu_head *head) 16961da177e4SLinus Torvalds { 16971da177e4SLinus Torvalds struct neigh_parms *parms = 16981da177e4SLinus Torvalds container_of(head, struct neigh_parms, rcu_head); 16991da177e4SLinus Torvalds 17001da177e4SLinus Torvalds neigh_parms_put(parms); 17011da177e4SLinus Torvalds } 17021da177e4SLinus Torvalds 17031da177e4SLinus Torvalds void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms) 17041da177e4SLinus Torvalds { 17051da177e4SLinus Torvalds if (!parms || parms == &tbl->parms) 17061da177e4SLinus Torvalds return; 17071da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 170875fbfd33SNicolas Dichtel list_del(&parms->list); 17091da177e4SLinus Torvalds parms->dead = 1; 17101da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 1711*d62607c3SJakub Kicinski netdev_put(parms->dev, &parms->dev_tracker); 17121da177e4SLinus Torvalds call_rcu(&parms->rcu_head, neigh_rcu_free_parms); 17131da177e4SLinus Torvalds } 17140a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_parms_release); 17151da177e4SLinus Torvalds 171606f0511dSDenis V. Lunev static void neigh_parms_destroy(struct neigh_parms *parms) 17171da177e4SLinus Torvalds { 17181da177e4SLinus Torvalds kfree(parms); 17191da177e4SLinus Torvalds } 17201da177e4SLinus Torvalds 1721c2ecba71SPavel Emelianov static struct lock_class_key neigh_table_proxy_queue_class; 1722c2ecba71SPavel Emelianov 1723d7480fd3SWANG Cong static struct neigh_table *neigh_tables[NEIGH_NR_TABLES] __read_mostly; 1724d7480fd3SWANG Cong 1725d7480fd3SWANG Cong void neigh_table_init(int index, struct neigh_table *tbl) 17261da177e4SLinus Torvalds { 17271da177e4SLinus Torvalds unsigned long now = jiffies; 17281da177e4SLinus Torvalds unsigned long phsize; 17291da177e4SLinus Torvalds 173075fbfd33SNicolas Dichtel INIT_LIST_HEAD(&tbl->parms_list); 173158956317SDavid Ahern INIT_LIST_HEAD(&tbl->gc_list); 17327482e384SDaniel Borkmann INIT_LIST_HEAD(&tbl->managed_list); 17337482e384SDaniel Borkmann 173475fbfd33SNicolas Dichtel list_add(&tbl->parms.list, &tbl->parms_list); 1735e42ea986SEric Dumazet write_pnet(&tbl->parms.net, &init_net); 17366343944bSReshetova, Elena refcount_set(&tbl->parms.refcnt, 1); 17371da177e4SLinus Torvalds tbl->parms.reachable_time = 17381f9248e5SJiri Pirko neigh_rand_reach_time(NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME)); 17391da177e4SLinus Torvalds 17401da177e4SLinus Torvalds tbl->stats = alloc_percpu(struct neigh_statistics); 17411da177e4SLinus Torvalds if (!tbl->stats) 17421da177e4SLinus Torvalds panic("cannot create neighbour cache statistics"); 17431da177e4SLinus Torvalds 17441da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 174571a5053aSChristoph Hellwig if (!proc_create_seq_data(tbl->id, 0, init_net.proc_net_stat, 174671a5053aSChristoph Hellwig &neigh_stat_seq_ops, tbl)) 17471da177e4SLinus Torvalds panic("cannot create neighbour proc dir entry"); 17481da177e4SLinus Torvalds #endif 17491da177e4SLinus Torvalds 1750cd089336SDavid S. Miller RCU_INIT_POINTER(tbl->nht, neigh_hash_alloc(3)); 17511da177e4SLinus Torvalds 17521da177e4SLinus Torvalds phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *); 175377d04bd9SAndrew Morton tbl->phash_buckets = kzalloc(phsize, GFP_KERNEL); 17541da177e4SLinus Torvalds 1755d6bf7817SEric Dumazet if (!tbl->nht || !tbl->phash_buckets) 17561da177e4SLinus Torvalds panic("cannot allocate neighbour cache hashes"); 17571da177e4SLinus Torvalds 175808433effSYOSHIFUJI Hideaki / 吉藤英明 if (!tbl->entry_size) 175908433effSYOSHIFUJI Hideaki / 吉藤英明 tbl->entry_size = ALIGN(offsetof(struct neighbour, primary_key) + 176008433effSYOSHIFUJI Hideaki / 吉藤英明 tbl->key_len, NEIGH_PRIV_ALIGN); 176108433effSYOSHIFUJI Hideaki / 吉藤英明 else 176208433effSYOSHIFUJI Hideaki / 吉藤英明 WARN_ON(tbl->entry_size % NEIGH_PRIV_ALIGN); 176308433effSYOSHIFUJI Hideaki / 吉藤英明 17641da177e4SLinus Torvalds rwlock_init(&tbl->lock); 17657482e384SDaniel Borkmann 1766203b42f7STejun Heo INIT_DEFERRABLE_WORK(&tbl->gc_work, neigh_periodic_work); 1767f618002bSviresh kumar queue_delayed_work(system_power_efficient_wq, &tbl->gc_work, 1768f618002bSviresh kumar tbl->parms.reachable_time); 17697482e384SDaniel Borkmann INIT_DEFERRABLE_WORK(&tbl->managed_work, neigh_managed_work); 17707482e384SDaniel Borkmann queue_delayed_work(system_power_efficient_wq, &tbl->managed_work, 0); 17717482e384SDaniel Borkmann 1772e99e88a9SKees Cook timer_setup(&tbl->proxy_timer, neigh_proxy_process, 0); 1773c2ecba71SPavel Emelianov skb_queue_head_init_class(&tbl->proxy_queue, 1774c2ecba71SPavel Emelianov &neigh_table_proxy_queue_class); 17751da177e4SLinus Torvalds 17761da177e4SLinus Torvalds tbl->last_flush = now; 17771da177e4SLinus Torvalds tbl->last_rand = now + tbl->parms.reachable_time * 20; 1778bd89efc5SSimon Kelley 1779d7480fd3SWANG Cong neigh_tables[index] = tbl; 17801da177e4SLinus Torvalds } 17810a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_table_init); 17821da177e4SLinus Torvalds 1783d7480fd3SWANG Cong int neigh_table_clear(int index, struct neigh_table *tbl) 17841da177e4SLinus Torvalds { 1785d7480fd3SWANG Cong neigh_tables[index] = NULL; 17861da177e4SLinus Torvalds /* It is not clean... Fix it to unload IPv6 module safely */ 17874177d5b0SDaniel Borkmann cancel_delayed_work_sync(&tbl->managed_work); 1788a5c30b34STejun Heo cancel_delayed_work_sync(&tbl->gc_work); 17891da177e4SLinus Torvalds del_timer_sync(&tbl->proxy_timer); 17901da177e4SLinus Torvalds pneigh_queue_purge(&tbl->proxy_queue); 17911da177e4SLinus Torvalds neigh_ifdown(tbl, NULL); 17921da177e4SLinus Torvalds if (atomic_read(&tbl->entries)) 1793e005d193SJoe Perches pr_crit("neighbour leakage\n"); 17941da177e4SLinus Torvalds 17956193d2beSEric Dumazet call_rcu(&rcu_dereference_protected(tbl->nht, 1)->rcu, 17966193d2beSEric Dumazet neigh_hash_free_rcu); 1797d6bf7817SEric Dumazet tbl->nht = NULL; 17981da177e4SLinus Torvalds 17991da177e4SLinus Torvalds kfree(tbl->phash_buckets); 18001da177e4SLinus Torvalds tbl->phash_buckets = NULL; 18011da177e4SLinus Torvalds 18023f192b5cSAlexey Dobriyan remove_proc_entry(tbl->id, init_net.proc_net_stat); 18033f192b5cSAlexey Dobriyan 18043fcde74bSKirill Korotaev free_percpu(tbl->stats); 18053fcde74bSKirill Korotaev tbl->stats = NULL; 18063fcde74bSKirill Korotaev 18071da177e4SLinus Torvalds return 0; 18081da177e4SLinus Torvalds } 18090a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_table_clear); 18101da177e4SLinus Torvalds 1811d7480fd3SWANG Cong static struct neigh_table *neigh_find_table(int family) 1812d7480fd3SWANG Cong { 1813d7480fd3SWANG Cong struct neigh_table *tbl = NULL; 1814d7480fd3SWANG Cong 1815d7480fd3SWANG Cong switch (family) { 1816d7480fd3SWANG Cong case AF_INET: 1817d7480fd3SWANG Cong tbl = neigh_tables[NEIGH_ARP_TABLE]; 1818d7480fd3SWANG Cong break; 1819d7480fd3SWANG Cong case AF_INET6: 1820d7480fd3SWANG Cong tbl = neigh_tables[NEIGH_ND_TABLE]; 1821d7480fd3SWANG Cong break; 1822d7480fd3SWANG Cong case AF_DECnet: 1823d7480fd3SWANG Cong tbl = neigh_tables[NEIGH_DN_TABLE]; 1824d7480fd3SWANG Cong break; 1825d7480fd3SWANG Cong } 1826d7480fd3SWANG Cong 1827d7480fd3SWANG Cong return tbl; 1828d7480fd3SWANG Cong } 1829d7480fd3SWANG Cong 183082cbb5c6SRoopa Prabhu const struct nla_policy nda_policy[NDA_MAX+1] = { 18311274e1ccSRoopa Prabhu [NDA_UNSPEC] = { .strict_start_type = NDA_NH_ID }, 183282cbb5c6SRoopa Prabhu [NDA_DST] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, 183382cbb5c6SRoopa Prabhu [NDA_LLADDR] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, 183482cbb5c6SRoopa Prabhu [NDA_CACHEINFO] = { .len = sizeof(struct nda_cacheinfo) }, 183582cbb5c6SRoopa Prabhu [NDA_PROBES] = { .type = NLA_U32 }, 183682cbb5c6SRoopa Prabhu [NDA_VLAN] = { .type = NLA_U16 }, 183782cbb5c6SRoopa Prabhu [NDA_PORT] = { .type = NLA_U16 }, 183882cbb5c6SRoopa Prabhu [NDA_VNI] = { .type = NLA_U32 }, 183982cbb5c6SRoopa Prabhu [NDA_IFINDEX] = { .type = NLA_U32 }, 184082cbb5c6SRoopa Prabhu [NDA_MASTER] = { .type = NLA_U32 }, 1841a9cd3439SDavid Ahern [NDA_PROTOCOL] = { .type = NLA_U8 }, 18421274e1ccSRoopa Prabhu [NDA_NH_ID] = { .type = NLA_U32 }, 1843c8e80c11SDaniel Borkmann [NDA_FLAGS_EXT] = NLA_POLICY_MASK(NLA_U32, NTF_EXT_MASK), 1844899426b3SNikolay Aleksandrov [NDA_FDB_EXT_ATTRS] = { .type = NLA_NESTED }, 184582cbb5c6SRoopa Prabhu }; 184682cbb5c6SRoopa Prabhu 1847c21ef3e3SDavid Ahern static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, 1848c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 18491da177e4SLinus Torvalds { 18503b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1851a14a49d2SThomas Graf struct ndmsg *ndm; 1852a14a49d2SThomas Graf struct nlattr *dst_attr; 18531da177e4SLinus Torvalds struct neigh_table *tbl; 1854d7480fd3SWANG Cong struct neighbour *neigh; 18551da177e4SLinus Torvalds struct net_device *dev = NULL; 1856a14a49d2SThomas Graf int err = -EINVAL; 18571da177e4SLinus Torvalds 1858110b2499SEric Dumazet ASSERT_RTNL(); 1859a14a49d2SThomas Graf if (nlmsg_len(nlh) < sizeof(*ndm)) 18601da177e4SLinus Torvalds goto out; 18611da177e4SLinus Torvalds 1862a14a49d2SThomas Graf dst_attr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_DST); 18637a35a50dSDavid Ahern if (!dst_attr) { 18647a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Network address not specified"); 1865a14a49d2SThomas Graf goto out; 18667a35a50dSDavid Ahern } 1867a14a49d2SThomas Graf 1868a14a49d2SThomas Graf ndm = nlmsg_data(nlh); 1869a14a49d2SThomas Graf if (ndm->ndm_ifindex) { 1870110b2499SEric Dumazet dev = __dev_get_by_index(net, ndm->ndm_ifindex); 1871a14a49d2SThomas Graf if (dev == NULL) { 1872a14a49d2SThomas Graf err = -ENODEV; 1873a14a49d2SThomas Graf goto out; 1874a14a49d2SThomas Graf } 1875a14a49d2SThomas Graf } 1876a14a49d2SThomas Graf 1877d7480fd3SWANG Cong tbl = neigh_find_table(ndm->ndm_family); 1878d7480fd3SWANG Cong if (tbl == NULL) 1879d7480fd3SWANG Cong return -EAFNOSUPPORT; 18801da177e4SLinus Torvalds 18817a35a50dSDavid Ahern if (nla_len(dst_attr) < (int)tbl->key_len) { 18827a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid network address"); 1883110b2499SEric Dumazet goto out; 18847a35a50dSDavid Ahern } 18851da177e4SLinus Torvalds 18861da177e4SLinus Torvalds if (ndm->ndm_flags & NTF_PROXY) { 1887426b5303SEric W. Biederman err = pneigh_delete(tbl, net, nla_data(dst_attr), dev); 1888110b2499SEric Dumazet goto out; 18891da177e4SLinus Torvalds } 18901da177e4SLinus Torvalds 1891a14a49d2SThomas Graf if (dev == NULL) 1892110b2499SEric Dumazet goto out; 18931da177e4SLinus Torvalds 1894a14a49d2SThomas Graf neigh = neigh_lookup(tbl, nla_data(dst_attr), dev); 1895a14a49d2SThomas Graf if (neigh == NULL) { 1896a14a49d2SThomas Graf err = -ENOENT; 1897110b2499SEric Dumazet goto out; 1898a14a49d2SThomas Graf } 1899a14a49d2SThomas Graf 19007a35a50dSDavid Ahern err = __neigh_update(neigh, NULL, NUD_FAILED, 19017a35a50dSDavid Ahern NEIGH_UPDATE_F_OVERRIDE | NEIGH_UPDATE_F_ADMIN, 19027a35a50dSDavid Ahern NETLINK_CB(skb).portid, extack); 19035071034eSSowmini Varadhan write_lock_bh(&tbl->lock); 1904a14a49d2SThomas Graf neigh_release(neigh); 19055071034eSSowmini Varadhan neigh_remove_one(neigh, tbl); 19065071034eSSowmini Varadhan write_unlock_bh(&tbl->lock); 1907a14a49d2SThomas Graf 19081da177e4SLinus Torvalds out: 19091da177e4SLinus Torvalds return err; 19101da177e4SLinus Torvalds } 19111da177e4SLinus Torvalds 1912c21ef3e3SDavid Ahern static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, 1913c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 19141da177e4SLinus Torvalds { 1915f7aa74e4SRoopa Prabhu int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE | 1916f7aa74e4SRoopa Prabhu NEIGH_UPDATE_F_OVERRIDE_ISROUTER; 19173b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 19185208debdSThomas Graf struct ndmsg *ndm; 19195208debdSThomas Graf struct nlattr *tb[NDA_MAX+1]; 19201da177e4SLinus Torvalds struct neigh_table *tbl; 19211da177e4SLinus Torvalds struct net_device *dev = NULL; 1922d7480fd3SWANG Cong struct neighbour *neigh; 1923d7480fd3SWANG Cong void *dst, *lladdr; 1924df9b0e30SDavid Ahern u8 protocol = 0; 19252c611ad9SRoopa Prabhu u32 ndm_flags; 19265208debdSThomas Graf int err; 19271da177e4SLinus Torvalds 1928110b2499SEric Dumazet ASSERT_RTNL(); 19298cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(*ndm), tb, NDA_MAX, 19308cb08174SJohannes Berg nda_policy, extack); 19315208debdSThomas Graf if (err < 0) 19321da177e4SLinus Torvalds goto out; 19331da177e4SLinus Torvalds 19345208debdSThomas Graf err = -EINVAL; 19357a35a50dSDavid Ahern if (!tb[NDA_DST]) { 19367a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Network address not specified"); 19375208debdSThomas Graf goto out; 19387a35a50dSDavid Ahern } 19395208debdSThomas Graf 19405208debdSThomas Graf ndm = nlmsg_data(nlh); 19412c611ad9SRoopa Prabhu ndm_flags = ndm->ndm_flags; 19422c611ad9SRoopa Prabhu if (tb[NDA_FLAGS_EXT]) { 19432c611ad9SRoopa Prabhu u32 ext = nla_get_u32(tb[NDA_FLAGS_EXT]); 19442c611ad9SRoopa Prabhu 1945507c2f1dSDaniel Borkmann BUILD_BUG_ON(sizeof(neigh->flags) * BITS_PER_BYTE < 1946507c2f1dSDaniel Borkmann (sizeof(ndm->ndm_flags) * BITS_PER_BYTE + 1947507c2f1dSDaniel Borkmann hweight32(NTF_EXT_MASK))); 19482c611ad9SRoopa Prabhu ndm_flags |= (ext << NTF_EXT_SHIFT); 19492c611ad9SRoopa Prabhu } 19505208debdSThomas Graf if (ndm->ndm_ifindex) { 1951110b2499SEric Dumazet dev = __dev_get_by_index(net, ndm->ndm_ifindex); 19525208debdSThomas Graf if (dev == NULL) { 19535208debdSThomas Graf err = -ENODEV; 19545208debdSThomas Graf goto out; 19555208debdSThomas Graf } 19565208debdSThomas Graf 19577a35a50dSDavid Ahern if (tb[NDA_LLADDR] && nla_len(tb[NDA_LLADDR]) < dev->addr_len) { 19587a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid link address"); 1959110b2499SEric Dumazet goto out; 19605208debdSThomas Graf } 19617a35a50dSDavid Ahern } 19625208debdSThomas Graf 1963d7480fd3SWANG Cong tbl = neigh_find_table(ndm->ndm_family); 1964d7480fd3SWANG Cong if (tbl == NULL) 1965d7480fd3SWANG Cong return -EAFNOSUPPORT; 19661da177e4SLinus Torvalds 19677a35a50dSDavid Ahern if (nla_len(tb[NDA_DST]) < (int)tbl->key_len) { 19687a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid network address"); 1969110b2499SEric Dumazet goto out; 19707a35a50dSDavid Ahern } 19717a35a50dSDavid Ahern 19725208debdSThomas Graf dst = nla_data(tb[NDA_DST]); 19735208debdSThomas Graf lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL; 19741da177e4SLinus Torvalds 1975a9cd3439SDavid Ahern if (tb[NDA_PROTOCOL]) 1976df9b0e30SDavid Ahern protocol = nla_get_u8(tb[NDA_PROTOCOL]); 19772c611ad9SRoopa Prabhu if (ndm_flags & NTF_PROXY) { 197862dd9318SVille Nuorvala struct pneigh_entry *pn; 197962dd9318SVille Nuorvala 19807482e384SDaniel Borkmann if (ndm_flags & NTF_MANAGED) { 19817482e384SDaniel Borkmann NL_SET_ERR_MSG(extack, "Invalid NTF_* flag combination"); 19827482e384SDaniel Borkmann goto out; 19837482e384SDaniel Borkmann } 19847482e384SDaniel Borkmann 19855208debdSThomas Graf err = -ENOBUFS; 1986426b5303SEric W. Biederman pn = pneigh_lookup(tbl, net, dst, dev, 1); 198762dd9318SVille Nuorvala if (pn) { 19882c611ad9SRoopa Prabhu pn->flags = ndm_flags; 1989df9b0e30SDavid Ahern if (protocol) 1990df9b0e30SDavid Ahern pn->protocol = protocol; 199162dd9318SVille Nuorvala err = 0; 199262dd9318SVille Nuorvala } 1993110b2499SEric Dumazet goto out; 19941da177e4SLinus Torvalds } 19951da177e4SLinus Torvalds 19967a35a50dSDavid Ahern if (!dev) { 19977a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Device not specified"); 1998110b2499SEric Dumazet goto out; 19997a35a50dSDavid Ahern } 20001da177e4SLinus Torvalds 2001b8fb1ab4SDavid Ahern if (tbl->allow_add && !tbl->allow_add(dev, extack)) { 2002b8fb1ab4SDavid Ahern err = -EINVAL; 2003b8fb1ab4SDavid Ahern goto out; 2004b8fb1ab4SDavid Ahern } 2005b8fb1ab4SDavid Ahern 20065208debdSThomas Graf neigh = neigh_lookup(tbl, dst, dev); 20075208debdSThomas Graf if (neigh == NULL) { 200830fc7efaSDaniel Borkmann bool ndm_permanent = ndm->ndm_state & NUD_PERMANENT; 200930fc7efaSDaniel Borkmann bool exempt_from_gc = ndm_permanent || 201030fc7efaSDaniel Borkmann ndm_flags & NTF_EXT_LEARNED; 2011e997f8a2SDavid Ahern 20125208debdSThomas Graf if (!(nlh->nlmsg_flags & NLM_F_CREATE)) { 20131da177e4SLinus Torvalds err = -ENOENT; 2014110b2499SEric Dumazet goto out; 20155208debdSThomas Graf } 201630fc7efaSDaniel Borkmann if (ndm_permanent && (ndm_flags & NTF_MANAGED)) { 201730fc7efaSDaniel Borkmann NL_SET_ERR_MSG(extack, "Invalid NTF_* flag for permanent entry"); 201830fc7efaSDaniel Borkmann err = -EINVAL; 201930fc7efaSDaniel Borkmann goto out; 202030fc7efaSDaniel Borkmann } 20215208debdSThomas Graf 2022e4400bbfSDaniel Borkmann neigh = ___neigh_create(tbl, dst, dev, 20237482e384SDaniel Borkmann ndm_flags & 20247482e384SDaniel Borkmann (NTF_EXT_LEARNED | NTF_MANAGED), 2025e4400bbfSDaniel Borkmann exempt_from_gc, true); 20265208debdSThomas Graf if (IS_ERR(neigh)) { 20275208debdSThomas Graf err = PTR_ERR(neigh); 2028110b2499SEric Dumazet goto out; 20291da177e4SLinus Torvalds } 20305208debdSThomas Graf } else { 20315208debdSThomas Graf if (nlh->nlmsg_flags & NLM_F_EXCL) { 20325208debdSThomas Graf err = -EEXIST; 20335208debdSThomas Graf neigh_release(neigh); 2034110b2499SEric Dumazet goto out; 20351da177e4SLinus Torvalds } 20361da177e4SLinus Torvalds 20375208debdSThomas Graf if (!(nlh->nlmsg_flags & NLM_F_REPLACE)) 2038f7aa74e4SRoopa Prabhu flags &= ~(NEIGH_UPDATE_F_OVERRIDE | 2039f7aa74e4SRoopa Prabhu NEIGH_UPDATE_F_OVERRIDE_ISROUTER); 20405208debdSThomas Graf } 20411da177e4SLinus Torvalds 204238212bb3SRoman Mashak if (protocol) 204338212bb3SRoman Mashak neigh->protocol = protocol; 20442c611ad9SRoopa Prabhu if (ndm_flags & NTF_EXT_LEARNED) 20459ce33e46SRoopa Prabhu flags |= NEIGH_UPDATE_F_EXT_LEARNED; 20462c611ad9SRoopa Prabhu if (ndm_flags & NTF_ROUTER) 2047f7aa74e4SRoopa Prabhu flags |= NEIGH_UPDATE_F_ISROUTER; 20487482e384SDaniel Borkmann if (ndm_flags & NTF_MANAGED) 20497482e384SDaniel Borkmann flags |= NEIGH_UPDATE_F_MANAGED; 20502c611ad9SRoopa Prabhu if (ndm_flags & NTF_USE) 20513dc20f47SDaniel Borkmann flags |= NEIGH_UPDATE_F_USE; 2052f7aa74e4SRoopa Prabhu 20537a35a50dSDavid Ahern err = __neigh_update(neigh, lladdr, ndm->ndm_state, flags, 20547a35a50dSDavid Ahern NETLINK_CB(skb).portid, extack); 20557482e384SDaniel Borkmann if (!err && ndm_flags & (NTF_USE | NTF_MANAGED)) { 20563dc20f47SDaniel Borkmann neigh_event_send(neigh, NULL); 20573dc20f47SDaniel Borkmann err = 0; 20583dc20f47SDaniel Borkmann } 20595208debdSThomas Graf neigh_release(neigh); 20601da177e4SLinus Torvalds out: 20611da177e4SLinus Torvalds return err; 20621da177e4SLinus Torvalds } 20631da177e4SLinus Torvalds 2064c7fb64dbSThomas Graf static int neightbl_fill_parms(struct sk_buff *skb, struct neigh_parms *parms) 2065c7fb64dbSThomas Graf { 2066ca860fb3SThomas Graf struct nlattr *nest; 2067e386c6ebSThomas Graf 2068ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, NDTA_PARMS); 2069ca860fb3SThomas Graf if (nest == NULL) 2070ca860fb3SThomas Graf return -ENOBUFS; 2071c7fb64dbSThomas Graf 20729a6308d7SDavid S. Miller if ((parms->dev && 20739a6308d7SDavid S. Miller nla_put_u32(skb, NDTPA_IFINDEX, parms->dev->ifindex)) || 20746343944bSReshetova, Elena nla_put_u32(skb, NDTPA_REFCNT, refcount_read(&parms->refcnt)) || 20751f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_QUEUE_LENBYTES, 20761f9248e5SJiri Pirko NEIGH_VAR(parms, QUEUE_LEN_BYTES)) || 20778b5c171bSEric Dumazet /* approximative value for deprecated QUEUE_LEN (in packets) */ 20789a6308d7SDavid S. Miller nla_put_u32(skb, NDTPA_QUEUE_LEN, 20791f9248e5SJiri Pirko NEIGH_VAR(parms, QUEUE_LEN_BYTES) / SKB_TRUESIZE(ETH_FRAME_LEN)) || 20801f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_PROXY_QLEN, NEIGH_VAR(parms, PROXY_QLEN)) || 20811f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_APP_PROBES, NEIGH_VAR(parms, APP_PROBES)) || 20821f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_UCAST_PROBES, 20831f9248e5SJiri Pirko NEIGH_VAR(parms, UCAST_PROBES)) || 20841f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_MCAST_PROBES, 20851f9248e5SJiri Pirko NEIGH_VAR(parms, MCAST_PROBES)) || 20868da86466SYOSHIFUJI Hideaki/吉藤英明 nla_put_u32(skb, NDTPA_MCAST_REPROBES, 20878da86466SYOSHIFUJI Hideaki/吉藤英明 NEIGH_VAR(parms, MCAST_REPROBES)) || 20882175d87cSNicolas Dichtel nla_put_msecs(skb, NDTPA_REACHABLE_TIME, parms->reachable_time, 20892175d87cSNicolas Dichtel NDTPA_PAD) || 20909a6308d7SDavid S. Miller nla_put_msecs(skb, NDTPA_BASE_REACHABLE_TIME, 20912175d87cSNicolas Dichtel NEIGH_VAR(parms, BASE_REACHABLE_TIME), NDTPA_PAD) || 20921f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_GC_STALETIME, 20932175d87cSNicolas Dichtel NEIGH_VAR(parms, GC_STALETIME), NDTPA_PAD) || 20949a6308d7SDavid S. Miller nla_put_msecs(skb, NDTPA_DELAY_PROBE_TIME, 20952175d87cSNicolas Dichtel NEIGH_VAR(parms, DELAY_PROBE_TIME), NDTPA_PAD) || 20961f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_RETRANS_TIME, 20972175d87cSNicolas Dichtel NEIGH_VAR(parms, RETRANS_TIME), NDTPA_PAD) || 20981f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_ANYCAST_DELAY, 20992175d87cSNicolas Dichtel NEIGH_VAR(parms, ANYCAST_DELAY), NDTPA_PAD) || 21001f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_PROXY_DELAY, 21012175d87cSNicolas Dichtel NEIGH_VAR(parms, PROXY_DELAY), NDTPA_PAD) || 21021f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_LOCKTIME, 21032175d87cSNicolas Dichtel NEIGH_VAR(parms, LOCKTIME), NDTPA_PAD)) 21049a6308d7SDavid S. Miller goto nla_put_failure; 2105ca860fb3SThomas Graf return nla_nest_end(skb, nest); 2106c7fb64dbSThomas Graf 2107ca860fb3SThomas Graf nla_put_failure: 2108bc3ed28cSThomas Graf nla_nest_cancel(skb, nest); 2109bc3ed28cSThomas Graf return -EMSGSIZE; 2110c7fb64dbSThomas Graf } 2111c7fb64dbSThomas Graf 2112ca860fb3SThomas Graf static int neightbl_fill_info(struct sk_buff *skb, struct neigh_table *tbl, 2113ca860fb3SThomas Graf u32 pid, u32 seq, int type, int flags) 2114c7fb64dbSThomas Graf { 2115c7fb64dbSThomas Graf struct nlmsghdr *nlh; 2116c7fb64dbSThomas Graf struct ndtmsg *ndtmsg; 2117c7fb64dbSThomas Graf 2118ca860fb3SThomas Graf nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndtmsg), flags); 2119ca860fb3SThomas Graf if (nlh == NULL) 212026932566SPatrick McHardy return -EMSGSIZE; 2121c7fb64dbSThomas Graf 2122ca860fb3SThomas Graf ndtmsg = nlmsg_data(nlh); 2123c7fb64dbSThomas Graf 2124c7fb64dbSThomas Graf read_lock_bh(&tbl->lock); 2125c7fb64dbSThomas Graf ndtmsg->ndtm_family = tbl->family; 21269ef1d4c7SPatrick McHardy ndtmsg->ndtm_pad1 = 0; 21279ef1d4c7SPatrick McHardy ndtmsg->ndtm_pad2 = 0; 2128c7fb64dbSThomas Graf 21299a6308d7SDavid S. Miller if (nla_put_string(skb, NDTA_NAME, tbl->id) || 21302175d87cSNicolas Dichtel nla_put_msecs(skb, NDTA_GC_INTERVAL, tbl->gc_interval, NDTA_PAD) || 21319a6308d7SDavid S. Miller nla_put_u32(skb, NDTA_THRESH1, tbl->gc_thresh1) || 21329a6308d7SDavid S. Miller nla_put_u32(skb, NDTA_THRESH2, tbl->gc_thresh2) || 21339a6308d7SDavid S. Miller nla_put_u32(skb, NDTA_THRESH3, tbl->gc_thresh3)) 21349a6308d7SDavid S. Miller goto nla_put_failure; 2135c7fb64dbSThomas Graf { 2136c7fb64dbSThomas Graf unsigned long now = jiffies; 21379d027e3aSEric Dumazet long flush_delta = now - tbl->last_flush; 21389d027e3aSEric Dumazet long rand_delta = now - tbl->last_rand; 2139d6bf7817SEric Dumazet struct neigh_hash_table *nht; 2140c7fb64dbSThomas Graf struct ndt_config ndc = { 2141c7fb64dbSThomas Graf .ndtc_key_len = tbl->key_len, 2142c7fb64dbSThomas Graf .ndtc_entry_size = tbl->entry_size, 2143c7fb64dbSThomas Graf .ndtc_entries = atomic_read(&tbl->entries), 2144c7fb64dbSThomas Graf .ndtc_last_flush = jiffies_to_msecs(flush_delta), 2145c7fb64dbSThomas Graf .ndtc_last_rand = jiffies_to_msecs(rand_delta), 2146c7fb64dbSThomas Graf .ndtc_proxy_qlen = tbl->proxy_queue.qlen, 2147c7fb64dbSThomas Graf }; 2148c7fb64dbSThomas Graf 2149d6bf7817SEric Dumazet rcu_read_lock_bh(); 2150d6bf7817SEric Dumazet nht = rcu_dereference_bh(tbl->nht); 21512c2aba6cSDavid S. Miller ndc.ndtc_hash_rnd = nht->hash_rnd[0]; 2152cd089336SDavid S. Miller ndc.ndtc_hash_mask = ((1 << nht->hash_shift) - 1); 2153d6bf7817SEric Dumazet rcu_read_unlock_bh(); 2154d6bf7817SEric Dumazet 21559a6308d7SDavid S. Miller if (nla_put(skb, NDTA_CONFIG, sizeof(ndc), &ndc)) 21569a6308d7SDavid S. Miller goto nla_put_failure; 2157c7fb64dbSThomas Graf } 2158c7fb64dbSThomas Graf 2159c7fb64dbSThomas Graf { 2160c7fb64dbSThomas Graf int cpu; 2161c7fb64dbSThomas Graf struct ndt_stats ndst; 2162c7fb64dbSThomas Graf 2163c7fb64dbSThomas Graf memset(&ndst, 0, sizeof(ndst)); 2164c7fb64dbSThomas Graf 21656f912042SKAMEZAWA Hiroyuki for_each_possible_cpu(cpu) { 2166c7fb64dbSThomas Graf struct neigh_statistics *st; 2167c7fb64dbSThomas Graf 2168c7fb64dbSThomas Graf st = per_cpu_ptr(tbl->stats, cpu); 2169c7fb64dbSThomas Graf ndst.ndts_allocs += st->allocs; 2170c7fb64dbSThomas Graf ndst.ndts_destroys += st->destroys; 2171c7fb64dbSThomas Graf ndst.ndts_hash_grows += st->hash_grows; 2172c7fb64dbSThomas Graf ndst.ndts_res_failed += st->res_failed; 2173c7fb64dbSThomas Graf ndst.ndts_lookups += st->lookups; 2174c7fb64dbSThomas Graf ndst.ndts_hits += st->hits; 2175c7fb64dbSThomas Graf ndst.ndts_rcv_probes_mcast += st->rcv_probes_mcast; 2176c7fb64dbSThomas Graf ndst.ndts_rcv_probes_ucast += st->rcv_probes_ucast; 2177c7fb64dbSThomas Graf ndst.ndts_periodic_gc_runs += st->periodic_gc_runs; 2178c7fb64dbSThomas Graf ndst.ndts_forced_gc_runs += st->forced_gc_runs; 2179fb811395SRick Jones ndst.ndts_table_fulls += st->table_fulls; 2180c7fb64dbSThomas Graf } 2181c7fb64dbSThomas Graf 2182b676338fSNicolas Dichtel if (nla_put_64bit(skb, NDTA_STATS, sizeof(ndst), &ndst, 2183b676338fSNicolas Dichtel NDTA_PAD)) 21849a6308d7SDavid S. Miller goto nla_put_failure; 2185c7fb64dbSThomas Graf } 2186c7fb64dbSThomas Graf 2187c7fb64dbSThomas Graf BUG_ON(tbl->parms.dev); 2188c7fb64dbSThomas Graf if (neightbl_fill_parms(skb, &tbl->parms) < 0) 2189ca860fb3SThomas Graf goto nla_put_failure; 2190c7fb64dbSThomas Graf 2191c7fb64dbSThomas Graf read_unlock_bh(&tbl->lock); 2192053c095aSJohannes Berg nlmsg_end(skb, nlh); 2193053c095aSJohannes Berg return 0; 2194c7fb64dbSThomas Graf 2195ca860fb3SThomas Graf nla_put_failure: 2196c7fb64dbSThomas Graf read_unlock_bh(&tbl->lock); 219726932566SPatrick McHardy nlmsg_cancel(skb, nlh); 219826932566SPatrick McHardy return -EMSGSIZE; 2199c7fb64dbSThomas Graf } 2200c7fb64dbSThomas Graf 2201ca860fb3SThomas Graf static int neightbl_fill_param_info(struct sk_buff *skb, 2202ca860fb3SThomas Graf struct neigh_table *tbl, 2203c7fb64dbSThomas Graf struct neigh_parms *parms, 2204ca860fb3SThomas Graf u32 pid, u32 seq, int type, 2205ca860fb3SThomas Graf unsigned int flags) 2206c7fb64dbSThomas Graf { 2207c7fb64dbSThomas Graf struct ndtmsg *ndtmsg; 2208c7fb64dbSThomas Graf struct nlmsghdr *nlh; 2209c7fb64dbSThomas Graf 2210ca860fb3SThomas Graf nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndtmsg), flags); 2211ca860fb3SThomas Graf if (nlh == NULL) 221226932566SPatrick McHardy return -EMSGSIZE; 2213c7fb64dbSThomas Graf 2214ca860fb3SThomas Graf ndtmsg = nlmsg_data(nlh); 2215c7fb64dbSThomas Graf 2216c7fb64dbSThomas Graf read_lock_bh(&tbl->lock); 2217c7fb64dbSThomas Graf ndtmsg->ndtm_family = tbl->family; 22189ef1d4c7SPatrick McHardy ndtmsg->ndtm_pad1 = 0; 22199ef1d4c7SPatrick McHardy ndtmsg->ndtm_pad2 = 0; 2220c7fb64dbSThomas Graf 2221ca860fb3SThomas Graf if (nla_put_string(skb, NDTA_NAME, tbl->id) < 0 || 2222ca860fb3SThomas Graf neightbl_fill_parms(skb, parms) < 0) 2223ca860fb3SThomas Graf goto errout; 2224c7fb64dbSThomas Graf 2225c7fb64dbSThomas Graf read_unlock_bh(&tbl->lock); 2226053c095aSJohannes Berg nlmsg_end(skb, nlh); 2227053c095aSJohannes Berg return 0; 2228ca860fb3SThomas Graf errout: 2229c7fb64dbSThomas Graf read_unlock_bh(&tbl->lock); 223026932566SPatrick McHardy nlmsg_cancel(skb, nlh); 223126932566SPatrick McHardy return -EMSGSIZE; 2232c7fb64dbSThomas Graf } 2233c7fb64dbSThomas Graf 2234ef7c79edSPatrick McHardy static const struct nla_policy nl_neightbl_policy[NDTA_MAX+1] = { 22356b3f8674SThomas Graf [NDTA_NAME] = { .type = NLA_STRING }, 22366b3f8674SThomas Graf [NDTA_THRESH1] = { .type = NLA_U32 }, 22376b3f8674SThomas Graf [NDTA_THRESH2] = { .type = NLA_U32 }, 22386b3f8674SThomas Graf [NDTA_THRESH3] = { .type = NLA_U32 }, 22396b3f8674SThomas Graf [NDTA_GC_INTERVAL] = { .type = NLA_U64 }, 22406b3f8674SThomas Graf [NDTA_PARMS] = { .type = NLA_NESTED }, 22416b3f8674SThomas Graf }; 22426b3f8674SThomas Graf 2243ef7c79edSPatrick McHardy static const struct nla_policy nl_ntbl_parm_policy[NDTPA_MAX+1] = { 22446b3f8674SThomas Graf [NDTPA_IFINDEX] = { .type = NLA_U32 }, 22456b3f8674SThomas Graf [NDTPA_QUEUE_LEN] = { .type = NLA_U32 }, 22466b3f8674SThomas Graf [NDTPA_PROXY_QLEN] = { .type = NLA_U32 }, 22476b3f8674SThomas Graf [NDTPA_APP_PROBES] = { .type = NLA_U32 }, 22486b3f8674SThomas Graf [NDTPA_UCAST_PROBES] = { .type = NLA_U32 }, 22496b3f8674SThomas Graf [NDTPA_MCAST_PROBES] = { .type = NLA_U32 }, 22508da86466SYOSHIFUJI Hideaki/吉藤英明 [NDTPA_MCAST_REPROBES] = { .type = NLA_U32 }, 22516b3f8674SThomas Graf [NDTPA_BASE_REACHABLE_TIME] = { .type = NLA_U64 }, 22526b3f8674SThomas Graf [NDTPA_GC_STALETIME] = { .type = NLA_U64 }, 22536b3f8674SThomas Graf [NDTPA_DELAY_PROBE_TIME] = { .type = NLA_U64 }, 22546b3f8674SThomas Graf [NDTPA_RETRANS_TIME] = { .type = NLA_U64 }, 22556b3f8674SThomas Graf [NDTPA_ANYCAST_DELAY] = { .type = NLA_U64 }, 22566b3f8674SThomas Graf [NDTPA_PROXY_DELAY] = { .type = NLA_U64 }, 22576b3f8674SThomas Graf [NDTPA_LOCKTIME] = { .type = NLA_U64 }, 22586b3f8674SThomas Graf }; 22596b3f8674SThomas Graf 2260c21ef3e3SDavid Ahern static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, 2261c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 2262c7fb64dbSThomas Graf { 22633b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 2264c7fb64dbSThomas Graf struct neigh_table *tbl; 22656b3f8674SThomas Graf struct ndtmsg *ndtmsg; 22666b3f8674SThomas Graf struct nlattr *tb[NDTA_MAX+1]; 2267d7480fd3SWANG Cong bool found = false; 2268d7480fd3SWANG Cong int err, tidx; 2269c7fb64dbSThomas Graf 22708cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(*ndtmsg), tb, NDTA_MAX, 2271c21ef3e3SDavid Ahern nl_neightbl_policy, extack); 22726b3f8674SThomas Graf if (err < 0) 22736b3f8674SThomas Graf goto errout; 2274c7fb64dbSThomas Graf 22756b3f8674SThomas Graf if (tb[NDTA_NAME] == NULL) { 22766b3f8674SThomas Graf err = -EINVAL; 22776b3f8674SThomas Graf goto errout; 22786b3f8674SThomas Graf } 22796b3f8674SThomas Graf 22806b3f8674SThomas Graf ndtmsg = nlmsg_data(nlh); 2281d7480fd3SWANG Cong 2282d7480fd3SWANG Cong for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) { 2283d7480fd3SWANG Cong tbl = neigh_tables[tidx]; 2284d7480fd3SWANG Cong if (!tbl) 2285d7480fd3SWANG Cong continue; 2286c7fb64dbSThomas Graf if (ndtmsg->ndtm_family && tbl->family != ndtmsg->ndtm_family) 2287c7fb64dbSThomas Graf continue; 2288d7480fd3SWANG Cong if (nla_strcmp(tb[NDTA_NAME], tbl->id) == 0) { 2289d7480fd3SWANG Cong found = true; 2290c7fb64dbSThomas Graf break; 2291c7fb64dbSThomas Graf } 2292c7fb64dbSThomas Graf } 2293c7fb64dbSThomas Graf 2294d7480fd3SWANG Cong if (!found) 2295d7480fd3SWANG Cong return -ENOENT; 2296d7480fd3SWANG Cong 2297c7fb64dbSThomas Graf /* 2298c7fb64dbSThomas Graf * We acquire tbl->lock to be nice to the periodic timers and 2299c7fb64dbSThomas Graf * make sure they always see a consistent set of values. 2300c7fb64dbSThomas Graf */ 2301c7fb64dbSThomas Graf write_lock_bh(&tbl->lock); 2302c7fb64dbSThomas Graf 23036b3f8674SThomas Graf if (tb[NDTA_PARMS]) { 23046b3f8674SThomas Graf struct nlattr *tbp[NDTPA_MAX+1]; 2305c7fb64dbSThomas Graf struct neigh_parms *p; 23066b3f8674SThomas Graf int i, ifindex = 0; 2307c7fb64dbSThomas Graf 23088cb08174SJohannes Berg err = nla_parse_nested_deprecated(tbp, NDTPA_MAX, 23098cb08174SJohannes Berg tb[NDTA_PARMS], 2310c21ef3e3SDavid Ahern nl_ntbl_parm_policy, extack); 23116b3f8674SThomas Graf if (err < 0) 23126b3f8674SThomas Graf goto errout_tbl_lock; 2313c7fb64dbSThomas Graf 23146b3f8674SThomas Graf if (tbp[NDTPA_IFINDEX]) 23156b3f8674SThomas Graf ifindex = nla_get_u32(tbp[NDTPA_IFINDEX]); 2316c7fb64dbSThomas Graf 231797fd5bc7STobias Klauser p = lookup_neigh_parms(tbl, net, ifindex); 2318c7fb64dbSThomas Graf if (p == NULL) { 2319c7fb64dbSThomas Graf err = -ENOENT; 23206b3f8674SThomas Graf goto errout_tbl_lock; 2321c7fb64dbSThomas Graf } 2322c7fb64dbSThomas Graf 23236b3f8674SThomas Graf for (i = 1; i <= NDTPA_MAX; i++) { 23246b3f8674SThomas Graf if (tbp[i] == NULL) 23256b3f8674SThomas Graf continue; 2326c7fb64dbSThomas Graf 23276b3f8674SThomas Graf switch (i) { 23286b3f8674SThomas Graf case NDTPA_QUEUE_LEN: 23291f9248e5SJiri Pirko NEIGH_VAR_SET(p, QUEUE_LEN_BYTES, 23301f9248e5SJiri Pirko nla_get_u32(tbp[i]) * 23311f9248e5SJiri Pirko SKB_TRUESIZE(ETH_FRAME_LEN)); 23328b5c171bSEric Dumazet break; 23338b5c171bSEric Dumazet case NDTPA_QUEUE_LENBYTES: 23341f9248e5SJiri Pirko NEIGH_VAR_SET(p, QUEUE_LEN_BYTES, 23351f9248e5SJiri Pirko nla_get_u32(tbp[i])); 23366b3f8674SThomas Graf break; 23376b3f8674SThomas Graf case NDTPA_PROXY_QLEN: 23381f9248e5SJiri Pirko NEIGH_VAR_SET(p, PROXY_QLEN, 23391f9248e5SJiri Pirko nla_get_u32(tbp[i])); 23406b3f8674SThomas Graf break; 23416b3f8674SThomas Graf case NDTPA_APP_PROBES: 23421f9248e5SJiri Pirko NEIGH_VAR_SET(p, APP_PROBES, 23431f9248e5SJiri Pirko nla_get_u32(tbp[i])); 23446b3f8674SThomas Graf break; 23456b3f8674SThomas Graf case NDTPA_UCAST_PROBES: 23461f9248e5SJiri Pirko NEIGH_VAR_SET(p, UCAST_PROBES, 23471f9248e5SJiri Pirko nla_get_u32(tbp[i])); 23486b3f8674SThomas Graf break; 23496b3f8674SThomas Graf case NDTPA_MCAST_PROBES: 23501f9248e5SJiri Pirko NEIGH_VAR_SET(p, MCAST_PROBES, 23511f9248e5SJiri Pirko nla_get_u32(tbp[i])); 23526b3f8674SThomas Graf break; 23538da86466SYOSHIFUJI Hideaki/吉藤英明 case NDTPA_MCAST_REPROBES: 23548da86466SYOSHIFUJI Hideaki/吉藤英明 NEIGH_VAR_SET(p, MCAST_REPROBES, 23558da86466SYOSHIFUJI Hideaki/吉藤英明 nla_get_u32(tbp[i])); 23568da86466SYOSHIFUJI Hideaki/吉藤英明 break; 23576b3f8674SThomas Graf case NDTPA_BASE_REACHABLE_TIME: 23581f9248e5SJiri Pirko NEIGH_VAR_SET(p, BASE_REACHABLE_TIME, 23591f9248e5SJiri Pirko nla_get_msecs(tbp[i])); 23604bf6980dSJean-Francois Remy /* update reachable_time as well, otherwise, the change will 23614bf6980dSJean-Francois Remy * only be effective after the next time neigh_periodic_work 23624bf6980dSJean-Francois Remy * decides to recompute it (can be multiple minutes) 23634bf6980dSJean-Francois Remy */ 23644bf6980dSJean-Francois Remy p->reachable_time = 23654bf6980dSJean-Francois Remy neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); 23666b3f8674SThomas Graf break; 23676b3f8674SThomas Graf case NDTPA_GC_STALETIME: 23681f9248e5SJiri Pirko NEIGH_VAR_SET(p, GC_STALETIME, 23691f9248e5SJiri Pirko nla_get_msecs(tbp[i])); 23706b3f8674SThomas Graf break; 23716b3f8674SThomas Graf case NDTPA_DELAY_PROBE_TIME: 23721f9248e5SJiri Pirko NEIGH_VAR_SET(p, DELAY_PROBE_TIME, 23731f9248e5SJiri Pirko nla_get_msecs(tbp[i])); 23742a4501aeSIdo Schimmel call_netevent_notifiers(NETEVENT_DELAY_PROBE_TIME_UPDATE, p); 23756b3f8674SThomas Graf break; 23766b3f8674SThomas Graf case NDTPA_RETRANS_TIME: 23771f9248e5SJiri Pirko NEIGH_VAR_SET(p, RETRANS_TIME, 23781f9248e5SJiri Pirko nla_get_msecs(tbp[i])); 23796b3f8674SThomas Graf break; 23806b3f8674SThomas Graf case NDTPA_ANYCAST_DELAY: 23813977458cSJiri Pirko NEIGH_VAR_SET(p, ANYCAST_DELAY, 23823977458cSJiri Pirko nla_get_msecs(tbp[i])); 23836b3f8674SThomas Graf break; 23846b3f8674SThomas Graf case NDTPA_PROXY_DELAY: 23853977458cSJiri Pirko NEIGH_VAR_SET(p, PROXY_DELAY, 23863977458cSJiri Pirko nla_get_msecs(tbp[i])); 23876b3f8674SThomas Graf break; 23886b3f8674SThomas Graf case NDTPA_LOCKTIME: 23893977458cSJiri Pirko NEIGH_VAR_SET(p, LOCKTIME, 23903977458cSJiri Pirko nla_get_msecs(tbp[i])); 23916b3f8674SThomas Graf break; 2392c7fb64dbSThomas Graf } 23936b3f8674SThomas Graf } 23946b3f8674SThomas Graf } 23956b3f8674SThomas Graf 2396dc25c676SGao feng err = -ENOENT; 2397dc25c676SGao feng if ((tb[NDTA_THRESH1] || tb[NDTA_THRESH2] || 2398dc25c676SGao feng tb[NDTA_THRESH3] || tb[NDTA_GC_INTERVAL]) && 2399dc25c676SGao feng !net_eq(net, &init_net)) 2400dc25c676SGao feng goto errout_tbl_lock; 2401dc25c676SGao feng 24026b3f8674SThomas Graf if (tb[NDTA_THRESH1]) 24036b3f8674SThomas Graf tbl->gc_thresh1 = nla_get_u32(tb[NDTA_THRESH1]); 24046b3f8674SThomas Graf 24056b3f8674SThomas Graf if (tb[NDTA_THRESH2]) 24066b3f8674SThomas Graf tbl->gc_thresh2 = nla_get_u32(tb[NDTA_THRESH2]); 24076b3f8674SThomas Graf 24086b3f8674SThomas Graf if (tb[NDTA_THRESH3]) 24096b3f8674SThomas Graf tbl->gc_thresh3 = nla_get_u32(tb[NDTA_THRESH3]); 24106b3f8674SThomas Graf 24116b3f8674SThomas Graf if (tb[NDTA_GC_INTERVAL]) 24126b3f8674SThomas Graf tbl->gc_interval = nla_get_msecs(tb[NDTA_GC_INTERVAL]); 2413c7fb64dbSThomas Graf 2414c7fb64dbSThomas Graf err = 0; 2415c7fb64dbSThomas Graf 24166b3f8674SThomas Graf errout_tbl_lock: 2417c7fb64dbSThomas Graf write_unlock_bh(&tbl->lock); 24186b3f8674SThomas Graf errout: 2419c7fb64dbSThomas Graf return err; 2420c7fb64dbSThomas Graf } 2421c7fb64dbSThomas Graf 24229632d47fSDavid Ahern static int neightbl_valid_dump_info(const struct nlmsghdr *nlh, 24239632d47fSDavid Ahern struct netlink_ext_ack *extack) 24249632d47fSDavid Ahern { 24259632d47fSDavid Ahern struct ndtmsg *ndtm; 24269632d47fSDavid Ahern 24279632d47fSDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndtm))) { 24289632d47fSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid header for neighbor table dump request"); 24299632d47fSDavid Ahern return -EINVAL; 24309632d47fSDavid Ahern } 24319632d47fSDavid Ahern 24329632d47fSDavid Ahern ndtm = nlmsg_data(nlh); 24339632d47fSDavid Ahern if (ndtm->ndtm_pad1 || ndtm->ndtm_pad2) { 24349632d47fSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor table dump request"); 24359632d47fSDavid Ahern return -EINVAL; 24369632d47fSDavid Ahern } 24379632d47fSDavid Ahern 24389632d47fSDavid Ahern if (nlmsg_attrlen(nlh, sizeof(*ndtm))) { 24399632d47fSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid data after header in neighbor table dump request"); 24409632d47fSDavid Ahern return -EINVAL; 24419632d47fSDavid Ahern } 24429632d47fSDavid Ahern 24439632d47fSDavid Ahern return 0; 24449632d47fSDavid Ahern } 24459632d47fSDavid Ahern 2446c8822a4eSThomas Graf static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb) 2447c7fb64dbSThomas Graf { 24489632d47fSDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 24493b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 2450ca860fb3SThomas Graf int family, tidx, nidx = 0; 2451ca860fb3SThomas Graf int tbl_skip = cb->args[0]; 2452ca860fb3SThomas Graf int neigh_skip = cb->args[1]; 2453c7fb64dbSThomas Graf struct neigh_table *tbl; 2454c7fb64dbSThomas Graf 24559632d47fSDavid Ahern if (cb->strict_check) { 24569632d47fSDavid Ahern int err = neightbl_valid_dump_info(nlh, cb->extack); 24579632d47fSDavid Ahern 24589632d47fSDavid Ahern if (err < 0) 24599632d47fSDavid Ahern return err; 24609632d47fSDavid Ahern } 24619632d47fSDavid Ahern 24629632d47fSDavid Ahern family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family; 2463c7fb64dbSThomas Graf 2464d7480fd3SWANG Cong for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) { 2465c7fb64dbSThomas Graf struct neigh_parms *p; 2466c7fb64dbSThomas Graf 2467d7480fd3SWANG Cong tbl = neigh_tables[tidx]; 2468d7480fd3SWANG Cong if (!tbl) 2469d7480fd3SWANG Cong continue; 2470d7480fd3SWANG Cong 2471ca860fb3SThomas Graf if (tidx < tbl_skip || (family && tbl->family != family)) 2472c7fb64dbSThomas Graf continue; 2473c7fb64dbSThomas Graf 247415e47304SEric W. Biederman if (neightbl_fill_info(skb, tbl, NETLINK_CB(cb->skb).portid, 24759632d47fSDavid Ahern nlh->nlmsg_seq, RTM_NEWNEIGHTBL, 24767b46a644SDavid S. Miller NLM_F_MULTI) < 0) 2477c7fb64dbSThomas Graf break; 2478c7fb64dbSThomas Graf 247975fbfd33SNicolas Dichtel nidx = 0; 248075fbfd33SNicolas Dichtel p = list_next_entry(&tbl->parms, list); 248175fbfd33SNicolas Dichtel list_for_each_entry_from(p, &tbl->parms_list, list) { 2482878628fbSYOSHIFUJI Hideaki if (!net_eq(neigh_parms_net(p), net)) 2483426b5303SEric W. Biederman continue; 2484426b5303SEric W. Biederman 2485efc683fcSGautam Kachroo if (nidx < neigh_skip) 2486efc683fcSGautam Kachroo goto next; 2487c7fb64dbSThomas Graf 2488ca860fb3SThomas Graf if (neightbl_fill_param_info(skb, tbl, p, 248915e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 24909632d47fSDavid Ahern nlh->nlmsg_seq, 2491ca860fb3SThomas Graf RTM_NEWNEIGHTBL, 24927b46a644SDavid S. Miller NLM_F_MULTI) < 0) 2493c7fb64dbSThomas Graf goto out; 2494efc683fcSGautam Kachroo next: 2495efc683fcSGautam Kachroo nidx++; 2496c7fb64dbSThomas Graf } 2497c7fb64dbSThomas Graf 2498ca860fb3SThomas Graf neigh_skip = 0; 2499c7fb64dbSThomas Graf } 2500c7fb64dbSThomas Graf out: 2501ca860fb3SThomas Graf cb->args[0] = tidx; 2502ca860fb3SThomas Graf cb->args[1] = nidx; 2503c7fb64dbSThomas Graf 2504c7fb64dbSThomas Graf return skb->len; 2505c7fb64dbSThomas Graf } 25061da177e4SLinus Torvalds 25078b8aec50SThomas Graf static int neigh_fill_info(struct sk_buff *skb, struct neighbour *neigh, 25088b8aec50SThomas Graf u32 pid, u32 seq, int type, unsigned int flags) 25091da177e4SLinus Torvalds { 25102c611ad9SRoopa Prabhu u32 neigh_flags, neigh_flags_ext; 25111da177e4SLinus Torvalds unsigned long now = jiffies; 25121da177e4SLinus Torvalds struct nda_cacheinfo ci; 25138b8aec50SThomas Graf struct nlmsghdr *nlh; 25148b8aec50SThomas Graf struct ndmsg *ndm; 25151da177e4SLinus Torvalds 25168b8aec50SThomas Graf nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags); 25178b8aec50SThomas Graf if (nlh == NULL) 251826932566SPatrick McHardy return -EMSGSIZE; 25198b8aec50SThomas Graf 25202c611ad9SRoopa Prabhu neigh_flags_ext = neigh->flags >> NTF_EXT_SHIFT; 25212c611ad9SRoopa Prabhu neigh_flags = neigh->flags & NTF_OLD_MASK; 25222c611ad9SRoopa Prabhu 25238b8aec50SThomas Graf ndm = nlmsg_data(nlh); 25248b8aec50SThomas Graf ndm->ndm_family = neigh->ops->family; 25259ef1d4c7SPatrick McHardy ndm->ndm_pad1 = 0; 25269ef1d4c7SPatrick McHardy ndm->ndm_pad2 = 0; 25272c611ad9SRoopa Prabhu ndm->ndm_flags = neigh_flags; 25288b8aec50SThomas Graf ndm->ndm_type = neigh->type; 25298b8aec50SThomas Graf ndm->ndm_ifindex = neigh->dev->ifindex; 25301da177e4SLinus Torvalds 25319a6308d7SDavid S. Miller if (nla_put(skb, NDA_DST, neigh->tbl->key_len, neigh->primary_key)) 25329a6308d7SDavid S. Miller goto nla_put_failure; 25338b8aec50SThomas Graf 25348b8aec50SThomas Graf read_lock_bh(&neigh->lock); 25358b8aec50SThomas Graf ndm->ndm_state = neigh->nud_state; 25360ed8ddf4SEric Dumazet if (neigh->nud_state & NUD_VALID) { 25370ed8ddf4SEric Dumazet char haddr[MAX_ADDR_LEN]; 25380ed8ddf4SEric Dumazet 25390ed8ddf4SEric Dumazet neigh_ha_snapshot(haddr, neigh, neigh->dev); 25400ed8ddf4SEric Dumazet if (nla_put(skb, NDA_LLADDR, neigh->dev->addr_len, haddr) < 0) { 25418b8aec50SThomas Graf read_unlock_bh(&neigh->lock); 25428b8aec50SThomas Graf goto nla_put_failure; 25438b8aec50SThomas Graf } 25440ed8ddf4SEric Dumazet } 25458b8aec50SThomas Graf 2546b9f5f52cSStephen Hemminger ci.ndm_used = jiffies_to_clock_t(now - neigh->used); 2547b9f5f52cSStephen Hemminger ci.ndm_confirmed = jiffies_to_clock_t(now - neigh->confirmed); 2548b9f5f52cSStephen Hemminger ci.ndm_updated = jiffies_to_clock_t(now - neigh->updated); 25499f237430SReshetova, Elena ci.ndm_refcnt = refcount_read(&neigh->refcnt) - 1; 25508b8aec50SThomas Graf read_unlock_bh(&neigh->lock); 25518b8aec50SThomas Graf 25529a6308d7SDavid S. Miller if (nla_put_u32(skb, NDA_PROBES, atomic_read(&neigh->probes)) || 25539a6308d7SDavid S. Miller nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci)) 25549a6308d7SDavid S. Miller goto nla_put_failure; 25558b8aec50SThomas Graf 2556df9b0e30SDavid Ahern if (neigh->protocol && nla_put_u8(skb, NDA_PROTOCOL, neigh->protocol)) 2557df9b0e30SDavid Ahern goto nla_put_failure; 25582c611ad9SRoopa Prabhu if (neigh_flags_ext && nla_put_u32(skb, NDA_FLAGS_EXT, neigh_flags_ext)) 25592c611ad9SRoopa Prabhu goto nla_put_failure; 2560df9b0e30SDavid Ahern 2561053c095aSJohannes Berg nlmsg_end(skb, nlh); 2562053c095aSJohannes Berg return 0; 25638b8aec50SThomas Graf 25648b8aec50SThomas Graf nla_put_failure: 256526932566SPatrick McHardy nlmsg_cancel(skb, nlh); 256626932566SPatrick McHardy return -EMSGSIZE; 25671da177e4SLinus Torvalds } 25681da177e4SLinus Torvalds 256984920c14STony Zelenoff static int pneigh_fill_info(struct sk_buff *skb, struct pneigh_entry *pn, 257084920c14STony Zelenoff u32 pid, u32 seq, int type, unsigned int flags, 257184920c14STony Zelenoff struct neigh_table *tbl) 257284920c14STony Zelenoff { 25732c611ad9SRoopa Prabhu u32 neigh_flags, neigh_flags_ext; 257484920c14STony Zelenoff struct nlmsghdr *nlh; 257584920c14STony Zelenoff struct ndmsg *ndm; 257684920c14STony Zelenoff 257784920c14STony Zelenoff nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags); 257884920c14STony Zelenoff if (nlh == NULL) 257984920c14STony Zelenoff return -EMSGSIZE; 258084920c14STony Zelenoff 25812c611ad9SRoopa Prabhu neigh_flags_ext = pn->flags >> NTF_EXT_SHIFT; 25822c611ad9SRoopa Prabhu neigh_flags = pn->flags & NTF_OLD_MASK; 25832c611ad9SRoopa Prabhu 258484920c14STony Zelenoff ndm = nlmsg_data(nlh); 258584920c14STony Zelenoff ndm->ndm_family = tbl->family; 258684920c14STony Zelenoff ndm->ndm_pad1 = 0; 258784920c14STony Zelenoff ndm->ndm_pad2 = 0; 25882c611ad9SRoopa Prabhu ndm->ndm_flags = neigh_flags | NTF_PROXY; 2589545469f7SJun Zhao ndm->ndm_type = RTN_UNICAST; 25906adc5fd6SKonstantin Khlebnikov ndm->ndm_ifindex = pn->dev ? pn->dev->ifindex : 0; 259184920c14STony Zelenoff ndm->ndm_state = NUD_NONE; 259284920c14STony Zelenoff 25939a6308d7SDavid S. Miller if (nla_put(skb, NDA_DST, tbl->key_len, pn->key)) 25949a6308d7SDavid S. Miller goto nla_put_failure; 259584920c14STony Zelenoff 2596df9b0e30SDavid Ahern if (pn->protocol && nla_put_u8(skb, NDA_PROTOCOL, pn->protocol)) 2597df9b0e30SDavid Ahern goto nla_put_failure; 25982c611ad9SRoopa Prabhu if (neigh_flags_ext && nla_put_u32(skb, NDA_FLAGS_EXT, neigh_flags_ext)) 25992c611ad9SRoopa Prabhu goto nla_put_failure; 2600df9b0e30SDavid Ahern 2601053c095aSJohannes Berg nlmsg_end(skb, nlh); 2602053c095aSJohannes Berg return 0; 260384920c14STony Zelenoff 260484920c14STony Zelenoff nla_put_failure: 260584920c14STony Zelenoff nlmsg_cancel(skb, nlh); 260684920c14STony Zelenoff return -EMSGSIZE; 260784920c14STony Zelenoff } 260884920c14STony Zelenoff 26097b8f7a40SRoopa Prabhu static void neigh_update_notify(struct neighbour *neigh, u32 nlmsg_pid) 2610d961db35SThomas Graf { 2611d961db35SThomas Graf call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh); 26127b8f7a40SRoopa Prabhu __neigh_notify(neigh, RTM_NEWNEIGH, 0, nlmsg_pid); 2613d961db35SThomas Graf } 26141da177e4SLinus Torvalds 261521fdd092SDavid Ahern static bool neigh_master_filtered(struct net_device *dev, int master_idx) 261621fdd092SDavid Ahern { 261721fdd092SDavid Ahern struct net_device *master; 261821fdd092SDavid Ahern 261921fdd092SDavid Ahern if (!master_idx) 262021fdd092SDavid Ahern return false; 262121fdd092SDavid Ahern 2622aab456dfSEric Dumazet master = dev ? netdev_master_upper_dev_get(dev) : NULL; 2623d3432bf1SLahav Schlesinger 2624d3432bf1SLahav Schlesinger /* 0 is already used to denote NDA_MASTER wasn't passed, therefore need another 2625d3432bf1SLahav Schlesinger * invalid value for ifindex to denote "no master". 2626d3432bf1SLahav Schlesinger */ 2627d3432bf1SLahav Schlesinger if (master_idx == -1) 2628d3432bf1SLahav Schlesinger return !!master; 2629d3432bf1SLahav Schlesinger 263021fdd092SDavid Ahern if (!master || master->ifindex != master_idx) 263121fdd092SDavid Ahern return true; 263221fdd092SDavid Ahern 263321fdd092SDavid Ahern return false; 263421fdd092SDavid Ahern } 263521fdd092SDavid Ahern 263616660f0bSDavid Ahern static bool neigh_ifindex_filtered(struct net_device *dev, int filter_idx) 263716660f0bSDavid Ahern { 2638aab456dfSEric Dumazet if (filter_idx && (!dev || dev->ifindex != filter_idx)) 263916660f0bSDavid Ahern return true; 264016660f0bSDavid Ahern 264116660f0bSDavid Ahern return false; 264216660f0bSDavid Ahern } 264316660f0bSDavid Ahern 26446f52f80eSDavid Ahern struct neigh_dump_filter { 26456f52f80eSDavid Ahern int master_idx; 26466f52f80eSDavid Ahern int dev_idx; 26476f52f80eSDavid Ahern }; 26486f52f80eSDavid Ahern 26491da177e4SLinus Torvalds static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, 26506f52f80eSDavid Ahern struct netlink_callback *cb, 26516f52f80eSDavid Ahern struct neigh_dump_filter *filter) 26521da177e4SLinus Torvalds { 26533b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 26541da177e4SLinus Torvalds struct neighbour *n; 26551da177e4SLinus Torvalds int rc, h, s_h = cb->args[1]; 26561da177e4SLinus Torvalds int idx, s_idx = idx = cb->args[2]; 2657d6bf7817SEric Dumazet struct neigh_hash_table *nht; 265821fdd092SDavid Ahern unsigned int flags = NLM_F_MULTI; 265921fdd092SDavid Ahern 26606f52f80eSDavid Ahern if (filter->dev_idx || filter->master_idx) 266121fdd092SDavid Ahern flags |= NLM_F_DUMP_FILTERED; 26621da177e4SLinus Torvalds 2663d6bf7817SEric Dumazet rcu_read_lock_bh(); 2664d6bf7817SEric Dumazet nht = rcu_dereference_bh(tbl->nht); 2665d6bf7817SEric Dumazet 26664bd6683bSEric Dumazet for (h = s_h; h < (1 << nht->hash_shift); h++) { 26671da177e4SLinus Torvalds if (h > s_h) 26681da177e4SLinus Torvalds s_idx = 0; 2669767e97e1SEric Dumazet for (n = rcu_dereference_bh(nht->hash_buckets[h]), idx = 0; 2670767e97e1SEric Dumazet n != NULL; 2671767e97e1SEric Dumazet n = rcu_dereference_bh(n->next)) { 267218502acdSZhang Shengju if (idx < s_idx || !net_eq(dev_net(n->dev), net)) 267318502acdSZhang Shengju goto next; 26746f52f80eSDavid Ahern if (neigh_ifindex_filtered(n->dev, filter->dev_idx) || 26756f52f80eSDavid Ahern neigh_master_filtered(n->dev, filter->master_idx)) 2676efc683fcSGautam Kachroo goto next; 267715e47304SEric W. Biederman if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid, 26781da177e4SLinus Torvalds cb->nlh->nlmsg_seq, 2679b6544c0bSJamal Hadi Salim RTM_NEWNEIGH, 268021fdd092SDavid Ahern flags) < 0) { 26811da177e4SLinus Torvalds rc = -1; 26821da177e4SLinus Torvalds goto out; 26831da177e4SLinus Torvalds } 2684efc683fcSGautam Kachroo next: 2685efc683fcSGautam Kachroo idx++; 26861da177e4SLinus Torvalds } 26871da177e4SLinus Torvalds } 26881da177e4SLinus Torvalds rc = skb->len; 26891da177e4SLinus Torvalds out: 2690d6bf7817SEric Dumazet rcu_read_unlock_bh(); 26911da177e4SLinus Torvalds cb->args[1] = h; 26921da177e4SLinus Torvalds cb->args[2] = idx; 26931da177e4SLinus Torvalds return rc; 26941da177e4SLinus Torvalds } 26951da177e4SLinus Torvalds 269684920c14STony Zelenoff static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, 26976f52f80eSDavid Ahern struct netlink_callback *cb, 26986f52f80eSDavid Ahern struct neigh_dump_filter *filter) 269984920c14STony Zelenoff { 270084920c14STony Zelenoff struct pneigh_entry *n; 270184920c14STony Zelenoff struct net *net = sock_net(skb->sk); 270284920c14STony Zelenoff int rc, h, s_h = cb->args[3]; 270384920c14STony Zelenoff int idx, s_idx = idx = cb->args[4]; 27046f52f80eSDavid Ahern unsigned int flags = NLM_F_MULTI; 27056f52f80eSDavid Ahern 27066f52f80eSDavid Ahern if (filter->dev_idx || filter->master_idx) 27076f52f80eSDavid Ahern flags |= NLM_F_DUMP_FILTERED; 270884920c14STony Zelenoff 270984920c14STony Zelenoff read_lock_bh(&tbl->lock); 271084920c14STony Zelenoff 27114bd6683bSEric Dumazet for (h = s_h; h <= PNEIGH_HASHMASK; h++) { 271284920c14STony Zelenoff if (h > s_h) 271384920c14STony Zelenoff s_idx = 0; 271484920c14STony Zelenoff for (n = tbl->phash_buckets[h], idx = 0; n; n = n->next) { 271518502acdSZhang Shengju if (idx < s_idx || pneigh_net(n) != net) 271684920c14STony Zelenoff goto next; 27176f52f80eSDavid Ahern if (neigh_ifindex_filtered(n->dev, filter->dev_idx) || 27186f52f80eSDavid Ahern neigh_master_filtered(n->dev, filter->master_idx)) 27196f52f80eSDavid Ahern goto next; 272015e47304SEric W. Biederman if (pneigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid, 272184920c14STony Zelenoff cb->nlh->nlmsg_seq, 27226f52f80eSDavid Ahern RTM_NEWNEIGH, flags, tbl) < 0) { 272384920c14STony Zelenoff read_unlock_bh(&tbl->lock); 272484920c14STony Zelenoff rc = -1; 272584920c14STony Zelenoff goto out; 272684920c14STony Zelenoff } 272784920c14STony Zelenoff next: 272884920c14STony Zelenoff idx++; 272984920c14STony Zelenoff } 273084920c14STony Zelenoff } 273184920c14STony Zelenoff 273284920c14STony Zelenoff read_unlock_bh(&tbl->lock); 273384920c14STony Zelenoff rc = skb->len; 273484920c14STony Zelenoff out: 273584920c14STony Zelenoff cb->args[3] = h; 273684920c14STony Zelenoff cb->args[4] = idx; 273784920c14STony Zelenoff return rc; 273884920c14STony Zelenoff 273984920c14STony Zelenoff } 274084920c14STony Zelenoff 274151183d23SDavid Ahern static int neigh_valid_dump_req(const struct nlmsghdr *nlh, 274251183d23SDavid Ahern bool strict_check, 274351183d23SDavid Ahern struct neigh_dump_filter *filter, 274451183d23SDavid Ahern struct netlink_ext_ack *extack) 274551183d23SDavid Ahern { 274651183d23SDavid Ahern struct nlattr *tb[NDA_MAX + 1]; 274751183d23SDavid Ahern int err, i; 274851183d23SDavid Ahern 274951183d23SDavid Ahern if (strict_check) { 275051183d23SDavid Ahern struct ndmsg *ndm; 275151183d23SDavid Ahern 275251183d23SDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) { 275351183d23SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid header for neighbor dump request"); 275451183d23SDavid Ahern return -EINVAL; 275551183d23SDavid Ahern } 275651183d23SDavid Ahern 275751183d23SDavid Ahern ndm = nlmsg_data(nlh); 275851183d23SDavid Ahern if (ndm->ndm_pad1 || ndm->ndm_pad2 || ndm->ndm_ifindex || 2759c0fde870SDavid Ahern ndm->ndm_state || ndm->ndm_type) { 276051183d23SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor dump request"); 276151183d23SDavid Ahern return -EINVAL; 276251183d23SDavid Ahern } 276351183d23SDavid Ahern 2764c0fde870SDavid Ahern if (ndm->ndm_flags & ~NTF_PROXY) { 2765c0fde870SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor dump request"); 2766c0fde870SDavid Ahern return -EINVAL; 2767c0fde870SDavid Ahern } 2768c0fde870SDavid Ahern 27698cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct ndmsg), 27708cb08174SJohannes Berg tb, NDA_MAX, nda_policy, 27718cb08174SJohannes Berg extack); 277251183d23SDavid Ahern } else { 27738cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(struct ndmsg), tb, 27748cb08174SJohannes Berg NDA_MAX, nda_policy, extack); 277551183d23SDavid Ahern } 277651183d23SDavid Ahern if (err < 0) 277751183d23SDavid Ahern return err; 277851183d23SDavid Ahern 277951183d23SDavid Ahern for (i = 0; i <= NDA_MAX; ++i) { 278051183d23SDavid Ahern if (!tb[i]) 278151183d23SDavid Ahern continue; 278251183d23SDavid Ahern 278351183d23SDavid Ahern /* all new attributes should require strict_check */ 278451183d23SDavid Ahern switch (i) { 278551183d23SDavid Ahern case NDA_IFINDEX: 278651183d23SDavid Ahern filter->dev_idx = nla_get_u32(tb[i]); 278751183d23SDavid Ahern break; 278851183d23SDavid Ahern case NDA_MASTER: 278951183d23SDavid Ahern filter->master_idx = nla_get_u32(tb[i]); 279051183d23SDavid Ahern break; 279151183d23SDavid Ahern default: 279251183d23SDavid Ahern if (strict_check) { 279351183d23SDavid Ahern NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor dump request"); 279451183d23SDavid Ahern return -EINVAL; 279551183d23SDavid Ahern } 279651183d23SDavid Ahern } 279751183d23SDavid Ahern } 279851183d23SDavid Ahern 279951183d23SDavid Ahern return 0; 280051183d23SDavid Ahern } 280151183d23SDavid Ahern 2802c8822a4eSThomas Graf static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb) 28031da177e4SLinus Torvalds { 28046f52f80eSDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 28056f52f80eSDavid Ahern struct neigh_dump_filter filter = {}; 28061da177e4SLinus Torvalds struct neigh_table *tbl; 28071da177e4SLinus Torvalds int t, family, s_t; 280884920c14STony Zelenoff int proxy = 0; 28094bd6683bSEric Dumazet int err; 28101da177e4SLinus Torvalds 28116f52f80eSDavid Ahern family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family; 281284920c14STony Zelenoff 281384920c14STony Zelenoff /* check for full ndmsg structure presence, family member is 281484920c14STony Zelenoff * the same for both structures 281584920c14STony Zelenoff */ 28166f52f80eSDavid Ahern if (nlmsg_len(nlh) >= sizeof(struct ndmsg) && 28176f52f80eSDavid Ahern ((struct ndmsg *)nlmsg_data(nlh))->ndm_flags == NTF_PROXY) 281884920c14STony Zelenoff proxy = 1; 281984920c14STony Zelenoff 282051183d23SDavid Ahern err = neigh_valid_dump_req(nlh, cb->strict_check, &filter, cb->extack); 282151183d23SDavid Ahern if (err < 0 && cb->strict_check) 282251183d23SDavid Ahern return err; 282351183d23SDavid Ahern 28241da177e4SLinus Torvalds s_t = cb->args[0]; 28251da177e4SLinus Torvalds 2826d7480fd3SWANG Cong for (t = 0; t < NEIGH_NR_TABLES; t++) { 2827d7480fd3SWANG Cong tbl = neigh_tables[t]; 2828d7480fd3SWANG Cong 2829d7480fd3SWANG Cong if (!tbl) 2830d7480fd3SWANG Cong continue; 28311da177e4SLinus Torvalds if (t < s_t || (family && tbl->family != family)) 28321da177e4SLinus Torvalds continue; 28331da177e4SLinus Torvalds if (t > s_t) 28341da177e4SLinus Torvalds memset(&cb->args[1], 0, sizeof(cb->args) - 28351da177e4SLinus Torvalds sizeof(cb->args[0])); 283684920c14STony Zelenoff if (proxy) 28376f52f80eSDavid Ahern err = pneigh_dump_table(tbl, skb, cb, &filter); 283884920c14STony Zelenoff else 28396f52f80eSDavid Ahern err = neigh_dump_table(tbl, skb, cb, &filter); 28404bd6683bSEric Dumazet if (err < 0) 28414bd6683bSEric Dumazet break; 28421da177e4SLinus Torvalds } 28431da177e4SLinus Torvalds 28441da177e4SLinus Torvalds cb->args[0] = t; 28451da177e4SLinus Torvalds return skb->len; 28461da177e4SLinus Torvalds } 28471da177e4SLinus Torvalds 284882cbb5c6SRoopa Prabhu static int neigh_valid_get_req(const struct nlmsghdr *nlh, 284982cbb5c6SRoopa Prabhu struct neigh_table **tbl, 285082cbb5c6SRoopa Prabhu void **dst, int *dev_idx, u8 *ndm_flags, 285182cbb5c6SRoopa Prabhu struct netlink_ext_ack *extack) 285282cbb5c6SRoopa Prabhu { 285382cbb5c6SRoopa Prabhu struct nlattr *tb[NDA_MAX + 1]; 285482cbb5c6SRoopa Prabhu struct ndmsg *ndm; 285582cbb5c6SRoopa Prabhu int err, i; 285682cbb5c6SRoopa Prabhu 285782cbb5c6SRoopa Prabhu if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) { 285882cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Invalid header for neighbor get request"); 285982cbb5c6SRoopa Prabhu return -EINVAL; 286082cbb5c6SRoopa Prabhu } 286182cbb5c6SRoopa Prabhu 286282cbb5c6SRoopa Prabhu ndm = nlmsg_data(nlh); 286382cbb5c6SRoopa Prabhu if (ndm->ndm_pad1 || ndm->ndm_pad2 || ndm->ndm_state || 286482cbb5c6SRoopa Prabhu ndm->ndm_type) { 286582cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor get request"); 286682cbb5c6SRoopa Prabhu return -EINVAL; 286782cbb5c6SRoopa Prabhu } 286882cbb5c6SRoopa Prabhu 286982cbb5c6SRoopa Prabhu if (ndm->ndm_flags & ~NTF_PROXY) { 287082cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor get request"); 287182cbb5c6SRoopa Prabhu return -EINVAL; 287282cbb5c6SRoopa Prabhu } 287382cbb5c6SRoopa Prabhu 28748cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct ndmsg), tb, 28758cb08174SJohannes Berg NDA_MAX, nda_policy, extack); 287682cbb5c6SRoopa Prabhu if (err < 0) 287782cbb5c6SRoopa Prabhu return err; 287882cbb5c6SRoopa Prabhu 287982cbb5c6SRoopa Prabhu *ndm_flags = ndm->ndm_flags; 288082cbb5c6SRoopa Prabhu *dev_idx = ndm->ndm_ifindex; 288182cbb5c6SRoopa Prabhu *tbl = neigh_find_table(ndm->ndm_family); 288282cbb5c6SRoopa Prabhu if (*tbl == NULL) { 288382cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Unsupported family in header for neighbor get request"); 288482cbb5c6SRoopa Prabhu return -EAFNOSUPPORT; 288582cbb5c6SRoopa Prabhu } 288682cbb5c6SRoopa Prabhu 288782cbb5c6SRoopa Prabhu for (i = 0; i <= NDA_MAX; ++i) { 288882cbb5c6SRoopa Prabhu if (!tb[i]) 288982cbb5c6SRoopa Prabhu continue; 289082cbb5c6SRoopa Prabhu 289182cbb5c6SRoopa Prabhu switch (i) { 289282cbb5c6SRoopa Prabhu case NDA_DST: 289382cbb5c6SRoopa Prabhu if (nla_len(tb[i]) != (int)(*tbl)->key_len) { 289482cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Invalid network address in neighbor get request"); 289582cbb5c6SRoopa Prabhu return -EINVAL; 289682cbb5c6SRoopa Prabhu } 289782cbb5c6SRoopa Prabhu *dst = nla_data(tb[i]); 289882cbb5c6SRoopa Prabhu break; 289982cbb5c6SRoopa Prabhu default: 290082cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor get request"); 290182cbb5c6SRoopa Prabhu return -EINVAL; 290282cbb5c6SRoopa Prabhu } 290382cbb5c6SRoopa Prabhu } 290482cbb5c6SRoopa Prabhu 290582cbb5c6SRoopa Prabhu return 0; 290682cbb5c6SRoopa Prabhu } 290782cbb5c6SRoopa Prabhu 290882cbb5c6SRoopa Prabhu static inline size_t neigh_nlmsg_size(void) 290982cbb5c6SRoopa Prabhu { 291082cbb5c6SRoopa Prabhu return NLMSG_ALIGN(sizeof(struct ndmsg)) 291182cbb5c6SRoopa Prabhu + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */ 291282cbb5c6SRoopa Prabhu + nla_total_size(MAX_ADDR_LEN) /* NDA_LLADDR */ 291382cbb5c6SRoopa Prabhu + nla_total_size(sizeof(struct nda_cacheinfo)) 291482cbb5c6SRoopa Prabhu + nla_total_size(4) /* NDA_PROBES */ 29152c611ad9SRoopa Prabhu + nla_total_size(4) /* NDA_FLAGS_EXT */ 291682cbb5c6SRoopa Prabhu + nla_total_size(1); /* NDA_PROTOCOL */ 291782cbb5c6SRoopa Prabhu } 291882cbb5c6SRoopa Prabhu 291982cbb5c6SRoopa Prabhu static int neigh_get_reply(struct net *net, struct neighbour *neigh, 292082cbb5c6SRoopa Prabhu u32 pid, u32 seq) 292182cbb5c6SRoopa Prabhu { 292282cbb5c6SRoopa Prabhu struct sk_buff *skb; 292382cbb5c6SRoopa Prabhu int err = 0; 292482cbb5c6SRoopa Prabhu 292582cbb5c6SRoopa Prabhu skb = nlmsg_new(neigh_nlmsg_size(), GFP_KERNEL); 292682cbb5c6SRoopa Prabhu if (!skb) 292782cbb5c6SRoopa Prabhu return -ENOBUFS; 292882cbb5c6SRoopa Prabhu 292982cbb5c6SRoopa Prabhu err = neigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0); 293082cbb5c6SRoopa Prabhu if (err) { 293182cbb5c6SRoopa Prabhu kfree_skb(skb); 293282cbb5c6SRoopa Prabhu goto errout; 293382cbb5c6SRoopa Prabhu } 293482cbb5c6SRoopa Prabhu 293582cbb5c6SRoopa Prabhu err = rtnl_unicast(skb, net, pid); 293682cbb5c6SRoopa Prabhu errout: 293782cbb5c6SRoopa Prabhu return err; 293882cbb5c6SRoopa Prabhu } 293982cbb5c6SRoopa Prabhu 294082cbb5c6SRoopa Prabhu static inline size_t pneigh_nlmsg_size(void) 294182cbb5c6SRoopa Prabhu { 294282cbb5c6SRoopa Prabhu return NLMSG_ALIGN(sizeof(struct ndmsg)) 2943463561e6SColin Ian King + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */ 29442c611ad9SRoopa Prabhu + nla_total_size(4) /* NDA_FLAGS_EXT */ 294582cbb5c6SRoopa Prabhu + nla_total_size(1); /* NDA_PROTOCOL */ 294682cbb5c6SRoopa Prabhu } 294782cbb5c6SRoopa Prabhu 294882cbb5c6SRoopa Prabhu static int pneigh_get_reply(struct net *net, struct pneigh_entry *neigh, 294982cbb5c6SRoopa Prabhu u32 pid, u32 seq, struct neigh_table *tbl) 295082cbb5c6SRoopa Prabhu { 295182cbb5c6SRoopa Prabhu struct sk_buff *skb; 295282cbb5c6SRoopa Prabhu int err = 0; 295382cbb5c6SRoopa Prabhu 295482cbb5c6SRoopa Prabhu skb = nlmsg_new(pneigh_nlmsg_size(), GFP_KERNEL); 295582cbb5c6SRoopa Prabhu if (!skb) 295682cbb5c6SRoopa Prabhu return -ENOBUFS; 295782cbb5c6SRoopa Prabhu 295882cbb5c6SRoopa Prabhu err = pneigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0, tbl); 295982cbb5c6SRoopa Prabhu if (err) { 296082cbb5c6SRoopa Prabhu kfree_skb(skb); 296182cbb5c6SRoopa Prabhu goto errout; 296282cbb5c6SRoopa Prabhu } 296382cbb5c6SRoopa Prabhu 296482cbb5c6SRoopa Prabhu err = rtnl_unicast(skb, net, pid); 296582cbb5c6SRoopa Prabhu errout: 296682cbb5c6SRoopa Prabhu return err; 296782cbb5c6SRoopa Prabhu } 296882cbb5c6SRoopa Prabhu 296982cbb5c6SRoopa Prabhu static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh, 297082cbb5c6SRoopa Prabhu struct netlink_ext_ack *extack) 297182cbb5c6SRoopa Prabhu { 297282cbb5c6SRoopa Prabhu struct net *net = sock_net(in_skb->sk); 297382cbb5c6SRoopa Prabhu struct net_device *dev = NULL; 297482cbb5c6SRoopa Prabhu struct neigh_table *tbl = NULL; 297582cbb5c6SRoopa Prabhu struct neighbour *neigh; 297682cbb5c6SRoopa Prabhu void *dst = NULL; 297782cbb5c6SRoopa Prabhu u8 ndm_flags = 0; 297882cbb5c6SRoopa Prabhu int dev_idx = 0; 297982cbb5c6SRoopa Prabhu int err; 298082cbb5c6SRoopa Prabhu 298182cbb5c6SRoopa Prabhu err = neigh_valid_get_req(nlh, &tbl, &dst, &dev_idx, &ndm_flags, 298282cbb5c6SRoopa Prabhu extack); 298382cbb5c6SRoopa Prabhu if (err < 0) 298482cbb5c6SRoopa Prabhu return err; 298582cbb5c6SRoopa Prabhu 298682cbb5c6SRoopa Prabhu if (dev_idx) { 298782cbb5c6SRoopa Prabhu dev = __dev_get_by_index(net, dev_idx); 298882cbb5c6SRoopa Prabhu if (!dev) { 298982cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Unknown device ifindex"); 299082cbb5c6SRoopa Prabhu return -ENODEV; 299182cbb5c6SRoopa Prabhu } 299282cbb5c6SRoopa Prabhu } 299382cbb5c6SRoopa Prabhu 299482cbb5c6SRoopa Prabhu if (!dst) { 299582cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Network address not specified"); 299682cbb5c6SRoopa Prabhu return -EINVAL; 299782cbb5c6SRoopa Prabhu } 299882cbb5c6SRoopa Prabhu 299982cbb5c6SRoopa Prabhu if (ndm_flags & NTF_PROXY) { 300082cbb5c6SRoopa Prabhu struct pneigh_entry *pn; 300182cbb5c6SRoopa Prabhu 300282cbb5c6SRoopa Prabhu pn = pneigh_lookup(tbl, net, dst, dev, 0); 300382cbb5c6SRoopa Prabhu if (!pn) { 300482cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Proxy neighbour entry not found"); 300582cbb5c6SRoopa Prabhu return -ENOENT; 300682cbb5c6SRoopa Prabhu } 300782cbb5c6SRoopa Prabhu return pneigh_get_reply(net, pn, NETLINK_CB(in_skb).portid, 300882cbb5c6SRoopa Prabhu nlh->nlmsg_seq, tbl); 300982cbb5c6SRoopa Prabhu } 301082cbb5c6SRoopa Prabhu 301182cbb5c6SRoopa Prabhu if (!dev) { 301282cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "No device specified"); 301382cbb5c6SRoopa Prabhu return -EINVAL; 301482cbb5c6SRoopa Prabhu } 301582cbb5c6SRoopa Prabhu 301682cbb5c6SRoopa Prabhu neigh = neigh_lookup(tbl, dst, dev); 301782cbb5c6SRoopa Prabhu if (!neigh) { 301882cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Neighbour entry not found"); 301982cbb5c6SRoopa Prabhu return -ENOENT; 302082cbb5c6SRoopa Prabhu } 302182cbb5c6SRoopa Prabhu 302282cbb5c6SRoopa Prabhu err = neigh_get_reply(net, neigh, NETLINK_CB(in_skb).portid, 302382cbb5c6SRoopa Prabhu nlh->nlmsg_seq); 302482cbb5c6SRoopa Prabhu 302582cbb5c6SRoopa Prabhu neigh_release(neigh); 302682cbb5c6SRoopa Prabhu 302782cbb5c6SRoopa Prabhu return err; 302882cbb5c6SRoopa Prabhu } 302982cbb5c6SRoopa Prabhu 30301da177e4SLinus Torvalds void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie) 30311da177e4SLinus Torvalds { 30321da177e4SLinus Torvalds int chain; 3033d6bf7817SEric Dumazet struct neigh_hash_table *nht; 30341da177e4SLinus Torvalds 3035d6bf7817SEric Dumazet rcu_read_lock_bh(); 3036d6bf7817SEric Dumazet nht = rcu_dereference_bh(tbl->nht); 3037d6bf7817SEric Dumazet 3038767e97e1SEric Dumazet read_lock(&tbl->lock); /* avoid resizes */ 3039cd089336SDavid S. Miller for (chain = 0; chain < (1 << nht->hash_shift); chain++) { 30401da177e4SLinus Torvalds struct neighbour *n; 30411da177e4SLinus Torvalds 3042767e97e1SEric Dumazet for (n = rcu_dereference_bh(nht->hash_buckets[chain]); 3043767e97e1SEric Dumazet n != NULL; 3044767e97e1SEric Dumazet n = rcu_dereference_bh(n->next)) 30451da177e4SLinus Torvalds cb(n, cookie); 30461da177e4SLinus Torvalds } 3047d6bf7817SEric Dumazet read_unlock(&tbl->lock); 3048d6bf7817SEric Dumazet rcu_read_unlock_bh(); 30491da177e4SLinus Torvalds } 30501da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_for_each); 30511da177e4SLinus Torvalds 30521da177e4SLinus Torvalds /* The tbl->lock must be held as a writer and BH disabled. */ 30531da177e4SLinus Torvalds void __neigh_for_each_release(struct neigh_table *tbl, 30541da177e4SLinus Torvalds int (*cb)(struct neighbour *)) 30551da177e4SLinus Torvalds { 30561da177e4SLinus Torvalds int chain; 3057d6bf7817SEric Dumazet struct neigh_hash_table *nht; 30581da177e4SLinus Torvalds 3059d6bf7817SEric Dumazet nht = rcu_dereference_protected(tbl->nht, 3060d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 3061cd089336SDavid S. Miller for (chain = 0; chain < (1 << nht->hash_shift); chain++) { 3062767e97e1SEric Dumazet struct neighbour *n; 3063767e97e1SEric Dumazet struct neighbour __rcu **np; 30641da177e4SLinus Torvalds 3065d6bf7817SEric Dumazet np = &nht->hash_buckets[chain]; 3066767e97e1SEric Dumazet while ((n = rcu_dereference_protected(*np, 3067767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))) != NULL) { 30681da177e4SLinus Torvalds int release; 30691da177e4SLinus Torvalds 30701da177e4SLinus Torvalds write_lock(&n->lock); 30711da177e4SLinus Torvalds release = cb(n); 30721da177e4SLinus Torvalds if (release) { 3073767e97e1SEric Dumazet rcu_assign_pointer(*np, 3074767e97e1SEric Dumazet rcu_dereference_protected(n->next, 3075767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))); 307658956317SDavid Ahern neigh_mark_dead(n); 30771da177e4SLinus Torvalds } else 30781da177e4SLinus Torvalds np = &n->next; 30791da177e4SLinus Torvalds write_unlock(&n->lock); 30804f494554SThomas Graf if (release) 30814f494554SThomas Graf neigh_cleanup_and_release(n); 30821da177e4SLinus Torvalds } 30831da177e4SLinus Torvalds } 3084ecbb4169SAlexey Kuznetsov } 30851da177e4SLinus Torvalds EXPORT_SYMBOL(__neigh_for_each_release); 30861da177e4SLinus Torvalds 3087b79bda3dSEric W. Biederman int neigh_xmit(int index, struct net_device *dev, 30884fd3d7d9SEric W. Biederman const void *addr, struct sk_buff *skb) 30894fd3d7d9SEric W. Biederman { 3090b79bda3dSEric W. Biederman int err = -EAFNOSUPPORT; 3091b79bda3dSEric W. Biederman if (likely(index < NEIGH_NR_TABLES)) { 30924fd3d7d9SEric W. Biederman struct neigh_table *tbl; 30934fd3d7d9SEric W. Biederman struct neighbour *neigh; 30944fd3d7d9SEric W. Biederman 3095b79bda3dSEric W. Biederman tbl = neigh_tables[index]; 30964fd3d7d9SEric W. Biederman if (!tbl) 30974fd3d7d9SEric W. Biederman goto out; 3098b560f03dSDavid Barroso rcu_read_lock_bh(); 30994b2a2bfeSDavid Ahern if (index == NEIGH_ARP_TABLE) { 31004b2a2bfeSDavid Ahern u32 key = *((u32 *)addr); 31014b2a2bfeSDavid Ahern 31024b2a2bfeSDavid Ahern neigh = __ipv4_neigh_lookup_noref(dev, key); 31034b2a2bfeSDavid Ahern } else { 31044fd3d7d9SEric W. Biederman neigh = __neigh_lookup_noref(tbl, addr, dev); 31054b2a2bfeSDavid Ahern } 31064fd3d7d9SEric W. Biederman if (!neigh) 31074fd3d7d9SEric W. Biederman neigh = __neigh_create(tbl, addr, dev, false); 31084fd3d7d9SEric W. Biederman err = PTR_ERR(neigh); 3109b560f03dSDavid Barroso if (IS_ERR(neigh)) { 3110b560f03dSDavid Barroso rcu_read_unlock_bh(); 31114fd3d7d9SEric W. Biederman goto out_kfree_skb; 3112b560f03dSDavid Barroso } 31134fd3d7d9SEric W. Biederman err = neigh->output(neigh, skb); 3114b560f03dSDavid Barroso rcu_read_unlock_bh(); 31154fd3d7d9SEric W. Biederman } 3116b79bda3dSEric W. Biederman else if (index == NEIGH_LINK_TABLE) { 3117b79bda3dSEric W. Biederman err = dev_hard_header(skb, dev, ntohs(skb->protocol), 3118b79bda3dSEric W. Biederman addr, NULL, skb->len); 3119b79bda3dSEric W. Biederman if (err < 0) 3120b79bda3dSEric W. Biederman goto out_kfree_skb; 3121b79bda3dSEric W. Biederman err = dev_queue_xmit(skb); 3122b79bda3dSEric W. Biederman } 31234fd3d7d9SEric W. Biederman out: 31244fd3d7d9SEric W. Biederman return err; 31254fd3d7d9SEric W. Biederman out_kfree_skb: 31264fd3d7d9SEric W. Biederman kfree_skb(skb); 31274fd3d7d9SEric W. Biederman goto out; 31284fd3d7d9SEric W. Biederman } 31294fd3d7d9SEric W. Biederman EXPORT_SYMBOL(neigh_xmit); 31304fd3d7d9SEric W. Biederman 31311da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 31321da177e4SLinus Torvalds 31331da177e4SLinus Torvalds static struct neighbour *neigh_get_first(struct seq_file *seq) 31341da177e4SLinus Torvalds { 31351da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 31361218854aSYOSHIFUJI Hideaki struct net *net = seq_file_net(seq); 3137d6bf7817SEric Dumazet struct neigh_hash_table *nht = state->nht; 31381da177e4SLinus Torvalds struct neighbour *n = NULL; 3139f530eed6SColin Ian King int bucket; 31401da177e4SLinus Torvalds 31411da177e4SLinus Torvalds state->flags &= ~NEIGH_SEQ_IS_PNEIGH; 3142cd089336SDavid S. Miller for (bucket = 0; bucket < (1 << nht->hash_shift); bucket++) { 3143767e97e1SEric Dumazet n = rcu_dereference_bh(nht->hash_buckets[bucket]); 31441da177e4SLinus Torvalds 31451da177e4SLinus Torvalds while (n) { 3146878628fbSYOSHIFUJI Hideaki if (!net_eq(dev_net(n->dev), net)) 3147426b5303SEric W. Biederman goto next; 31481da177e4SLinus Torvalds if (state->neigh_sub_iter) { 31491da177e4SLinus Torvalds loff_t fakep = 0; 31501da177e4SLinus Torvalds void *v; 31511da177e4SLinus Torvalds 31521da177e4SLinus Torvalds v = state->neigh_sub_iter(state, n, &fakep); 31531da177e4SLinus Torvalds if (!v) 31541da177e4SLinus Torvalds goto next; 31551da177e4SLinus Torvalds } 31561da177e4SLinus Torvalds if (!(state->flags & NEIGH_SEQ_SKIP_NOARP)) 31571da177e4SLinus Torvalds break; 31581da177e4SLinus Torvalds if (n->nud_state & ~NUD_NOARP) 31591da177e4SLinus Torvalds break; 31601da177e4SLinus Torvalds next: 3161767e97e1SEric Dumazet n = rcu_dereference_bh(n->next); 31621da177e4SLinus Torvalds } 31631da177e4SLinus Torvalds 31641da177e4SLinus Torvalds if (n) 31651da177e4SLinus Torvalds break; 31661da177e4SLinus Torvalds } 31671da177e4SLinus Torvalds state->bucket = bucket; 31681da177e4SLinus Torvalds 31691da177e4SLinus Torvalds return n; 31701da177e4SLinus Torvalds } 31711da177e4SLinus Torvalds 31721da177e4SLinus Torvalds static struct neighbour *neigh_get_next(struct seq_file *seq, 31731da177e4SLinus Torvalds struct neighbour *n, 31741da177e4SLinus Torvalds loff_t *pos) 31751da177e4SLinus Torvalds { 31761da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 31771218854aSYOSHIFUJI Hideaki struct net *net = seq_file_net(seq); 3178d6bf7817SEric Dumazet struct neigh_hash_table *nht = state->nht; 31791da177e4SLinus Torvalds 31801da177e4SLinus Torvalds if (state->neigh_sub_iter) { 31811da177e4SLinus Torvalds void *v = state->neigh_sub_iter(state, n, pos); 31821da177e4SLinus Torvalds if (v) 31831da177e4SLinus Torvalds return n; 31841da177e4SLinus Torvalds } 3185767e97e1SEric Dumazet n = rcu_dereference_bh(n->next); 31861da177e4SLinus Torvalds 31871da177e4SLinus Torvalds while (1) { 31881da177e4SLinus Torvalds while (n) { 3189878628fbSYOSHIFUJI Hideaki if (!net_eq(dev_net(n->dev), net)) 3190426b5303SEric W. Biederman goto next; 31911da177e4SLinus Torvalds if (state->neigh_sub_iter) { 31921da177e4SLinus Torvalds void *v = state->neigh_sub_iter(state, n, pos); 31931da177e4SLinus Torvalds if (v) 31941da177e4SLinus Torvalds return n; 31951da177e4SLinus Torvalds goto next; 31961da177e4SLinus Torvalds } 31971da177e4SLinus Torvalds if (!(state->flags & NEIGH_SEQ_SKIP_NOARP)) 31981da177e4SLinus Torvalds break; 31991da177e4SLinus Torvalds 32001da177e4SLinus Torvalds if (n->nud_state & ~NUD_NOARP) 32011da177e4SLinus Torvalds break; 32021da177e4SLinus Torvalds next: 3203767e97e1SEric Dumazet n = rcu_dereference_bh(n->next); 32041da177e4SLinus Torvalds } 32051da177e4SLinus Torvalds 32061da177e4SLinus Torvalds if (n) 32071da177e4SLinus Torvalds break; 32081da177e4SLinus Torvalds 3209cd089336SDavid S. Miller if (++state->bucket >= (1 << nht->hash_shift)) 32101da177e4SLinus Torvalds break; 32111da177e4SLinus Torvalds 3212767e97e1SEric Dumazet n = rcu_dereference_bh(nht->hash_buckets[state->bucket]); 32131da177e4SLinus Torvalds } 32141da177e4SLinus Torvalds 32151da177e4SLinus Torvalds if (n && pos) 32161da177e4SLinus Torvalds --(*pos); 32171da177e4SLinus Torvalds return n; 32181da177e4SLinus Torvalds } 32191da177e4SLinus Torvalds 32201da177e4SLinus Torvalds static struct neighbour *neigh_get_idx(struct seq_file *seq, loff_t *pos) 32211da177e4SLinus Torvalds { 32221da177e4SLinus Torvalds struct neighbour *n = neigh_get_first(seq); 32231da177e4SLinus Torvalds 32241da177e4SLinus Torvalds if (n) { 3225745e2031SChris Larson --(*pos); 32261da177e4SLinus Torvalds while (*pos) { 32271da177e4SLinus Torvalds n = neigh_get_next(seq, n, pos); 32281da177e4SLinus Torvalds if (!n) 32291da177e4SLinus Torvalds break; 32301da177e4SLinus Torvalds } 32311da177e4SLinus Torvalds } 32321da177e4SLinus Torvalds return *pos ? NULL : n; 32331da177e4SLinus Torvalds } 32341da177e4SLinus Torvalds 32351da177e4SLinus Torvalds static struct pneigh_entry *pneigh_get_first(struct seq_file *seq) 32361da177e4SLinus Torvalds { 32371da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 32381218854aSYOSHIFUJI Hideaki struct net *net = seq_file_net(seq); 32391da177e4SLinus Torvalds struct neigh_table *tbl = state->tbl; 32401da177e4SLinus Torvalds struct pneigh_entry *pn = NULL; 324148de7c0cSYang Li int bucket; 32421da177e4SLinus Torvalds 32431da177e4SLinus Torvalds state->flags |= NEIGH_SEQ_IS_PNEIGH; 32441da177e4SLinus Torvalds for (bucket = 0; bucket <= PNEIGH_HASHMASK; bucket++) { 32451da177e4SLinus Torvalds pn = tbl->phash_buckets[bucket]; 3246878628fbSYOSHIFUJI Hideaki while (pn && !net_eq(pneigh_net(pn), net)) 3247426b5303SEric W. Biederman pn = pn->next; 32481da177e4SLinus Torvalds if (pn) 32491da177e4SLinus Torvalds break; 32501da177e4SLinus Torvalds } 32511da177e4SLinus Torvalds state->bucket = bucket; 32521da177e4SLinus Torvalds 32531da177e4SLinus Torvalds return pn; 32541da177e4SLinus Torvalds } 32551da177e4SLinus Torvalds 32561da177e4SLinus Torvalds static struct pneigh_entry *pneigh_get_next(struct seq_file *seq, 32571da177e4SLinus Torvalds struct pneigh_entry *pn, 32581da177e4SLinus Torvalds loff_t *pos) 32591da177e4SLinus Torvalds { 32601da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 32611218854aSYOSHIFUJI Hideaki struct net *net = seq_file_net(seq); 32621da177e4SLinus Torvalds struct neigh_table *tbl = state->tbl; 32631da177e4SLinus Torvalds 3264df07a94cSJorge Boncompte [DTI2] do { 32651da177e4SLinus Torvalds pn = pn->next; 3266df07a94cSJorge Boncompte [DTI2] } while (pn && !net_eq(pneigh_net(pn), net)); 3267df07a94cSJorge Boncompte [DTI2] 32681da177e4SLinus Torvalds while (!pn) { 32691da177e4SLinus Torvalds if (++state->bucket > PNEIGH_HASHMASK) 32701da177e4SLinus Torvalds break; 32711da177e4SLinus Torvalds pn = tbl->phash_buckets[state->bucket]; 3272878628fbSYOSHIFUJI Hideaki while (pn && !net_eq(pneigh_net(pn), net)) 3273426b5303SEric W. Biederman pn = pn->next; 32741da177e4SLinus Torvalds if (pn) 32751da177e4SLinus Torvalds break; 32761da177e4SLinus Torvalds } 32771da177e4SLinus Torvalds 32781da177e4SLinus Torvalds if (pn && pos) 32791da177e4SLinus Torvalds --(*pos); 32801da177e4SLinus Torvalds 32811da177e4SLinus Torvalds return pn; 32821da177e4SLinus Torvalds } 32831da177e4SLinus Torvalds 32841da177e4SLinus Torvalds static struct pneigh_entry *pneigh_get_idx(struct seq_file *seq, loff_t *pos) 32851da177e4SLinus Torvalds { 32861da177e4SLinus Torvalds struct pneigh_entry *pn = pneigh_get_first(seq); 32871da177e4SLinus Torvalds 32881da177e4SLinus Torvalds if (pn) { 3289745e2031SChris Larson --(*pos); 32901da177e4SLinus Torvalds while (*pos) { 32911da177e4SLinus Torvalds pn = pneigh_get_next(seq, pn, pos); 32921da177e4SLinus Torvalds if (!pn) 32931da177e4SLinus Torvalds break; 32941da177e4SLinus Torvalds } 32951da177e4SLinus Torvalds } 32961da177e4SLinus Torvalds return *pos ? NULL : pn; 32971da177e4SLinus Torvalds } 32981da177e4SLinus Torvalds 32991da177e4SLinus Torvalds static void *neigh_get_idx_any(struct seq_file *seq, loff_t *pos) 33001da177e4SLinus Torvalds { 33011da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 33021da177e4SLinus Torvalds void *rc; 3303745e2031SChris Larson loff_t idxpos = *pos; 33041da177e4SLinus Torvalds 3305745e2031SChris Larson rc = neigh_get_idx(seq, &idxpos); 33061da177e4SLinus Torvalds if (!rc && !(state->flags & NEIGH_SEQ_NEIGH_ONLY)) 3307745e2031SChris Larson rc = pneigh_get_idx(seq, &idxpos); 33081da177e4SLinus Torvalds 33091da177e4SLinus Torvalds return rc; 33101da177e4SLinus Torvalds } 33111da177e4SLinus Torvalds 33121da177e4SLinus Torvalds void *neigh_seq_start(struct seq_file *seq, loff_t *pos, struct neigh_table *tbl, unsigned int neigh_seq_flags) 3313f3e92cb8SEric Dumazet __acquires(tbl->lock) 3314d6bf7817SEric Dumazet __acquires(rcu_bh) 33151da177e4SLinus Torvalds { 33161da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 33171da177e4SLinus Torvalds 33181da177e4SLinus Torvalds state->tbl = tbl; 33191da177e4SLinus Torvalds state->bucket = 0; 33201da177e4SLinus Torvalds state->flags = (neigh_seq_flags & ~NEIGH_SEQ_IS_PNEIGH); 33211da177e4SLinus Torvalds 3322d6bf7817SEric Dumazet rcu_read_lock_bh(); 3323d6bf7817SEric Dumazet state->nht = rcu_dereference_bh(tbl->nht); 3324f3e92cb8SEric Dumazet read_lock(&tbl->lock); 3325767e97e1SEric Dumazet 3326745e2031SChris Larson return *pos ? neigh_get_idx_any(seq, pos) : SEQ_START_TOKEN; 33271da177e4SLinus Torvalds } 33281da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_seq_start); 33291da177e4SLinus Torvalds 33301da177e4SLinus Torvalds void *neigh_seq_next(struct seq_file *seq, void *v, loff_t *pos) 33311da177e4SLinus Torvalds { 33321da177e4SLinus Torvalds struct neigh_seq_state *state; 33331da177e4SLinus Torvalds void *rc; 33341da177e4SLinus Torvalds 33351da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 3336bff69732SChris Larson rc = neigh_get_first(seq); 33371da177e4SLinus Torvalds goto out; 33381da177e4SLinus Torvalds } 33391da177e4SLinus Torvalds 33401da177e4SLinus Torvalds state = seq->private; 33411da177e4SLinus Torvalds if (!(state->flags & NEIGH_SEQ_IS_PNEIGH)) { 33421da177e4SLinus Torvalds rc = neigh_get_next(seq, v, NULL); 33431da177e4SLinus Torvalds if (rc) 33441da177e4SLinus Torvalds goto out; 33451da177e4SLinus Torvalds if (!(state->flags & NEIGH_SEQ_NEIGH_ONLY)) 33461da177e4SLinus Torvalds rc = pneigh_get_first(seq); 33471da177e4SLinus Torvalds } else { 33481da177e4SLinus Torvalds BUG_ON(state->flags & NEIGH_SEQ_NEIGH_ONLY); 33491da177e4SLinus Torvalds rc = pneigh_get_next(seq, v, NULL); 33501da177e4SLinus Torvalds } 33511da177e4SLinus Torvalds out: 33521da177e4SLinus Torvalds ++(*pos); 33531da177e4SLinus Torvalds return rc; 33541da177e4SLinus Torvalds } 33551da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_seq_next); 33561da177e4SLinus Torvalds 33571da177e4SLinus Torvalds void neigh_seq_stop(struct seq_file *seq, void *v) 3358f3e92cb8SEric Dumazet __releases(tbl->lock) 3359d6bf7817SEric Dumazet __releases(rcu_bh) 33601da177e4SLinus Torvalds { 3361f3e92cb8SEric Dumazet struct neigh_seq_state *state = seq->private; 3362f3e92cb8SEric Dumazet struct neigh_table *tbl = state->tbl; 3363f3e92cb8SEric Dumazet 3364f3e92cb8SEric Dumazet read_unlock(&tbl->lock); 3365d6bf7817SEric Dumazet rcu_read_unlock_bh(); 33661da177e4SLinus Torvalds } 33671da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_seq_stop); 33681da177e4SLinus Torvalds 33691da177e4SLinus Torvalds /* statistics via seq_file */ 33701da177e4SLinus Torvalds 33711da177e4SLinus Torvalds static void *neigh_stat_seq_start(struct seq_file *seq, loff_t *pos) 33721da177e4SLinus Torvalds { 3373359745d7SMuchun Song struct neigh_table *tbl = pde_data(file_inode(seq->file)); 33741da177e4SLinus Torvalds int cpu; 33751da177e4SLinus Torvalds 33761da177e4SLinus Torvalds if (*pos == 0) 33771da177e4SLinus Torvalds return SEQ_START_TOKEN; 33781da177e4SLinus Torvalds 33790f23174aSRusty Russell for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) { 33801da177e4SLinus Torvalds if (!cpu_possible(cpu)) 33811da177e4SLinus Torvalds continue; 33821da177e4SLinus Torvalds *pos = cpu+1; 33831da177e4SLinus Torvalds return per_cpu_ptr(tbl->stats, cpu); 33841da177e4SLinus Torvalds } 33851da177e4SLinus Torvalds return NULL; 33861da177e4SLinus Torvalds } 33871da177e4SLinus Torvalds 33881da177e4SLinus Torvalds static void *neigh_stat_seq_next(struct seq_file *seq, void *v, loff_t *pos) 33891da177e4SLinus Torvalds { 3390359745d7SMuchun Song struct neigh_table *tbl = pde_data(file_inode(seq->file)); 33911da177e4SLinus Torvalds int cpu; 33921da177e4SLinus Torvalds 33930f23174aSRusty Russell for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) { 33941da177e4SLinus Torvalds if (!cpu_possible(cpu)) 33951da177e4SLinus Torvalds continue; 33961da177e4SLinus Torvalds *pos = cpu+1; 33971da177e4SLinus Torvalds return per_cpu_ptr(tbl->stats, cpu); 33981da177e4SLinus Torvalds } 33991e3f9f07SVasily Averin (*pos)++; 34001da177e4SLinus Torvalds return NULL; 34011da177e4SLinus Torvalds } 34021da177e4SLinus Torvalds 34031da177e4SLinus Torvalds static void neigh_stat_seq_stop(struct seq_file *seq, void *v) 34041da177e4SLinus Torvalds { 34051da177e4SLinus Torvalds 34061da177e4SLinus Torvalds } 34071da177e4SLinus Torvalds 34081da177e4SLinus Torvalds static int neigh_stat_seq_show(struct seq_file *seq, void *v) 34091da177e4SLinus Torvalds { 3410359745d7SMuchun Song struct neigh_table *tbl = pde_data(file_inode(seq->file)); 34111da177e4SLinus Torvalds struct neigh_statistics *st = v; 34121da177e4SLinus Torvalds 34131da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 34140547ffe6SYajun 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"); 34151da177e4SLinus Torvalds return 0; 34161da177e4SLinus Torvalds } 34171da177e4SLinus Torvalds 34181da177e4SLinus Torvalds seq_printf(seq, "%08x %08lx %08lx %08lx %08lx %08lx %08lx " 34190547ffe6SYajun Deng "%08lx %08lx %08lx " 34200547ffe6SYajun Deng "%08lx %08lx %08lx\n", 34211da177e4SLinus Torvalds atomic_read(&tbl->entries), 34221da177e4SLinus Torvalds 34231da177e4SLinus Torvalds st->allocs, 34241da177e4SLinus Torvalds st->destroys, 34251da177e4SLinus Torvalds st->hash_grows, 34261da177e4SLinus Torvalds 34271da177e4SLinus Torvalds st->lookups, 34281da177e4SLinus Torvalds st->hits, 34291da177e4SLinus Torvalds 34301da177e4SLinus Torvalds st->res_failed, 34311da177e4SLinus Torvalds 34321da177e4SLinus Torvalds st->rcv_probes_mcast, 34331da177e4SLinus Torvalds st->rcv_probes_ucast, 34341da177e4SLinus Torvalds 34351da177e4SLinus Torvalds st->periodic_gc_runs, 34369a6d276eSNeil Horman st->forced_gc_runs, 3437fb811395SRick Jones st->unres_discards, 3438fb811395SRick Jones st->table_fulls 34391da177e4SLinus Torvalds ); 34401da177e4SLinus Torvalds 34411da177e4SLinus Torvalds return 0; 34421da177e4SLinus Torvalds } 34431da177e4SLinus Torvalds 3444f690808eSStephen Hemminger static const struct seq_operations neigh_stat_seq_ops = { 34451da177e4SLinus Torvalds .start = neigh_stat_seq_start, 34461da177e4SLinus Torvalds .next = neigh_stat_seq_next, 34471da177e4SLinus Torvalds .stop = neigh_stat_seq_stop, 34481da177e4SLinus Torvalds .show = neigh_stat_seq_show, 34491da177e4SLinus Torvalds }; 34501da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 34511da177e4SLinus Torvalds 34527b8f7a40SRoopa Prabhu static void __neigh_notify(struct neighbour *n, int type, int flags, 34537b8f7a40SRoopa Prabhu u32 pid) 34541da177e4SLinus Torvalds { 3455c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(n->dev); 34568b8aec50SThomas Graf struct sk_buff *skb; 3457b8673311SThomas Graf int err = -ENOBUFS; 34581da177e4SLinus Torvalds 3459339bf98fSThomas Graf skb = nlmsg_new(neigh_nlmsg_size(), GFP_ATOMIC); 34608b8aec50SThomas Graf if (skb == NULL) 3461b8673311SThomas Graf goto errout; 34621da177e4SLinus Torvalds 34637b8f7a40SRoopa Prabhu err = neigh_fill_info(skb, n, pid, 0, type, flags); 346426932566SPatrick McHardy if (err < 0) { 346526932566SPatrick McHardy /* -EMSGSIZE implies BUG in neigh_nlmsg_size() */ 346626932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 346726932566SPatrick McHardy kfree_skb(skb); 346826932566SPatrick McHardy goto errout; 346926932566SPatrick McHardy } 34701ce85fe4SPablo Neira Ayuso rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); 34711ce85fe4SPablo Neira Ayuso return; 3472b8673311SThomas Graf errout: 3473b8673311SThomas Graf if (err < 0) 3474426b5303SEric W. Biederman rtnl_set_sk_err(net, RTNLGRP_NEIGH, err); 3475b8673311SThomas Graf } 3476b8673311SThomas Graf 3477b8673311SThomas Graf void neigh_app_ns(struct neighbour *n) 3478b8673311SThomas Graf { 34797b8f7a40SRoopa Prabhu __neigh_notify(n, RTM_GETNEIGH, NLM_F_REQUEST, 0); 34808b8aec50SThomas Graf } 34810a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_app_ns); 34821da177e4SLinus Torvalds 34831da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 3484b93196dcSCong Wang static int unres_qlen_max = INT_MAX / SKB_TRUESIZE(ETH_FRAME_LEN); 34851da177e4SLinus Torvalds 3486fe2c6338SJoe Perches static int proc_unres_qlen(struct ctl_table *ctl, int write, 348732927393SChristoph Hellwig void *buffer, size_t *lenp, loff_t *ppos) 34888b5c171bSEric Dumazet { 34898b5c171bSEric Dumazet int size, ret; 3490fe2c6338SJoe Perches struct ctl_table tmp = *ctl; 34918b5c171bSEric Dumazet 3492eec4844fSMatteo Croce tmp.extra1 = SYSCTL_ZERO; 3493ce46cc64SShan Wei tmp.extra2 = &unres_qlen_max; 34948b5c171bSEric Dumazet tmp.data = &size; 3495ce46cc64SShan Wei 3496ce46cc64SShan Wei size = *(int *)ctl->data / SKB_TRUESIZE(ETH_FRAME_LEN); 3497ce46cc64SShan Wei ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); 3498ce46cc64SShan Wei 34998b5c171bSEric Dumazet if (write && !ret) 35008b5c171bSEric Dumazet *(int *)ctl->data = size * SKB_TRUESIZE(ETH_FRAME_LEN); 35018b5c171bSEric Dumazet return ret; 35028b5c171bSEric Dumazet } 35038b5c171bSEric Dumazet 35041d4c8c29SJiri Pirko static struct neigh_parms *neigh_get_dev_parms_rcu(struct net_device *dev, 35051d4c8c29SJiri Pirko int family) 35061d4c8c29SJiri Pirko { 3507bba24896SJiri Pirko switch (family) { 3508bba24896SJiri Pirko case AF_INET: 35091d4c8c29SJiri Pirko return __in_dev_arp_parms_get_rcu(dev); 3510bba24896SJiri Pirko case AF_INET6: 3511bba24896SJiri Pirko return __in6_dev_nd_parms_get_rcu(dev); 3512bba24896SJiri Pirko } 35131d4c8c29SJiri Pirko return NULL; 35141d4c8c29SJiri Pirko } 35151d4c8c29SJiri Pirko 35161d4c8c29SJiri Pirko static void neigh_copy_dflt_parms(struct net *net, struct neigh_parms *p, 35171d4c8c29SJiri Pirko int index) 35181d4c8c29SJiri Pirko { 35191d4c8c29SJiri Pirko struct net_device *dev; 35201d4c8c29SJiri Pirko int family = neigh_parms_family(p); 35211d4c8c29SJiri Pirko 35221d4c8c29SJiri Pirko rcu_read_lock(); 35231d4c8c29SJiri Pirko for_each_netdev_rcu(net, dev) { 35241d4c8c29SJiri Pirko struct neigh_parms *dst_p = 35251d4c8c29SJiri Pirko neigh_get_dev_parms_rcu(dev, family); 35261d4c8c29SJiri Pirko 35271d4c8c29SJiri Pirko if (dst_p && !test_bit(index, dst_p->data_state)) 35281d4c8c29SJiri Pirko dst_p->data[index] = p->data[index]; 35291d4c8c29SJiri Pirko } 35301d4c8c29SJiri Pirko rcu_read_unlock(); 35311d4c8c29SJiri Pirko } 35321d4c8c29SJiri Pirko 35331d4c8c29SJiri Pirko static void neigh_proc_update(struct ctl_table *ctl, int write) 35341d4c8c29SJiri Pirko { 35351d4c8c29SJiri Pirko struct net_device *dev = ctl->extra1; 35361d4c8c29SJiri Pirko struct neigh_parms *p = ctl->extra2; 353777d47afbSJiri Pirko struct net *net = neigh_parms_net(p); 35381d4c8c29SJiri Pirko int index = (int *) ctl->data - p->data; 35391d4c8c29SJiri Pirko 35401d4c8c29SJiri Pirko if (!write) 35411d4c8c29SJiri Pirko return; 35421d4c8c29SJiri Pirko 35431d4c8c29SJiri Pirko set_bit(index, p->data_state); 35447627ae60SMarcus Huewe if (index == NEIGH_VAR_DELAY_PROBE_TIME) 35452a4501aeSIdo Schimmel call_netevent_notifiers(NETEVENT_DELAY_PROBE_TIME_UPDATE, p); 35461d4c8c29SJiri Pirko if (!dev) /* NULL dev means this is default value */ 35471d4c8c29SJiri Pirko neigh_copy_dflt_parms(net, p, index); 35481d4c8c29SJiri Pirko } 35491d4c8c29SJiri Pirko 35501f9248e5SJiri Pirko static int neigh_proc_dointvec_zero_intmax(struct ctl_table *ctl, int write, 355132927393SChristoph Hellwig void *buffer, size_t *lenp, 355232927393SChristoph Hellwig loff_t *ppos) 35531f9248e5SJiri Pirko { 35541f9248e5SJiri Pirko struct ctl_table tmp = *ctl; 35551d4c8c29SJiri Pirko int ret; 35561f9248e5SJiri Pirko 3557eec4844fSMatteo Croce tmp.extra1 = SYSCTL_ZERO; 3558eec4844fSMatteo Croce tmp.extra2 = SYSCTL_INT_MAX; 35591f9248e5SJiri Pirko 35601d4c8c29SJiri Pirko ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); 35611d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 35621d4c8c29SJiri Pirko return ret; 35631f9248e5SJiri Pirko } 35641f9248e5SJiri Pirko 356532927393SChristoph Hellwig int neigh_proc_dointvec(struct ctl_table *ctl, int write, void *buffer, 356632927393SChristoph Hellwig size_t *lenp, loff_t *ppos) 3567cb5b09c1SJiri Pirko { 35681d4c8c29SJiri Pirko int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 35691d4c8c29SJiri Pirko 35701d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 35711d4c8c29SJiri Pirko return ret; 3572cb5b09c1SJiri Pirko } 3573cb5b09c1SJiri Pirko EXPORT_SYMBOL(neigh_proc_dointvec); 3574cb5b09c1SJiri Pirko 357532927393SChristoph Hellwig int neigh_proc_dointvec_jiffies(struct ctl_table *ctl, int write, void *buffer, 3576cb5b09c1SJiri Pirko size_t *lenp, loff_t *ppos) 3577cb5b09c1SJiri Pirko { 35781d4c8c29SJiri Pirko int ret = proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos); 35791d4c8c29SJiri Pirko 35801d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 35811d4c8c29SJiri Pirko return ret; 3582cb5b09c1SJiri Pirko } 3583cb5b09c1SJiri Pirko EXPORT_SYMBOL(neigh_proc_dointvec_jiffies); 3584cb5b09c1SJiri Pirko 3585cb5b09c1SJiri Pirko static int neigh_proc_dointvec_userhz_jiffies(struct ctl_table *ctl, int write, 358632927393SChristoph Hellwig void *buffer, size_t *lenp, 358732927393SChristoph Hellwig loff_t *ppos) 3588cb5b09c1SJiri Pirko { 35891d4c8c29SJiri Pirko int ret = proc_dointvec_userhz_jiffies(ctl, write, buffer, lenp, ppos); 35901d4c8c29SJiri Pirko 35911d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 35921d4c8c29SJiri Pirko return ret; 3593cb5b09c1SJiri Pirko } 3594cb5b09c1SJiri Pirko 3595cb5b09c1SJiri Pirko int neigh_proc_dointvec_ms_jiffies(struct ctl_table *ctl, int write, 359632927393SChristoph Hellwig void *buffer, size_t *lenp, loff_t *ppos) 3597cb5b09c1SJiri Pirko { 35981d4c8c29SJiri Pirko int ret = proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos); 35991d4c8c29SJiri Pirko 36001d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 36011d4c8c29SJiri Pirko return ret; 3602cb5b09c1SJiri Pirko } 3603cb5b09c1SJiri Pirko EXPORT_SYMBOL(neigh_proc_dointvec_ms_jiffies); 3604cb5b09c1SJiri Pirko 3605cb5b09c1SJiri Pirko static int neigh_proc_dointvec_unres_qlen(struct ctl_table *ctl, int write, 360632927393SChristoph Hellwig void *buffer, size_t *lenp, 360732927393SChristoph Hellwig loff_t *ppos) 3608cb5b09c1SJiri Pirko { 36091d4c8c29SJiri Pirko int ret = proc_unres_qlen(ctl, write, buffer, lenp, ppos); 36101d4c8c29SJiri Pirko 36111d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 36121d4c8c29SJiri Pirko return ret; 3613cb5b09c1SJiri Pirko } 3614cb5b09c1SJiri Pirko 36154bf6980dSJean-Francois Remy static int neigh_proc_base_reachable_time(struct ctl_table *ctl, int write, 361632927393SChristoph Hellwig void *buffer, size_t *lenp, 361732927393SChristoph Hellwig loff_t *ppos) 36184bf6980dSJean-Francois Remy { 36194bf6980dSJean-Francois Remy struct neigh_parms *p = ctl->extra2; 36204bf6980dSJean-Francois Remy int ret; 36214bf6980dSJean-Francois Remy 36224bf6980dSJean-Francois Remy if (strcmp(ctl->procname, "base_reachable_time") == 0) 36234bf6980dSJean-Francois Remy ret = neigh_proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos); 36244bf6980dSJean-Francois Remy else if (strcmp(ctl->procname, "base_reachable_time_ms") == 0) 36254bf6980dSJean-Francois Remy ret = neigh_proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos); 36264bf6980dSJean-Francois Remy else 36274bf6980dSJean-Francois Remy ret = -1; 36284bf6980dSJean-Francois Remy 36294bf6980dSJean-Francois Remy if (write && ret == 0) { 36304bf6980dSJean-Francois Remy /* update reachable_time as well, otherwise, the change will 36314bf6980dSJean-Francois Remy * only be effective after the next time neigh_periodic_work 36324bf6980dSJean-Francois Remy * decides to recompute it 36334bf6980dSJean-Francois Remy */ 36344bf6980dSJean-Francois Remy p->reachable_time = 36354bf6980dSJean-Francois Remy neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); 36364bf6980dSJean-Francois Remy } 36374bf6980dSJean-Francois Remy return ret; 36384bf6980dSJean-Francois Remy } 36394bf6980dSJean-Francois Remy 36401f9248e5SJiri Pirko #define NEIGH_PARMS_DATA_OFFSET(index) \ 36411f9248e5SJiri Pirko (&((struct neigh_parms *) 0)->data[index]) 36421f9248e5SJiri Pirko 36431f9248e5SJiri Pirko #define NEIGH_SYSCTL_ENTRY(attr, data_attr, name, mval, proc) \ 36441f9248e5SJiri Pirko [NEIGH_VAR_ ## attr] = { \ 36451f9248e5SJiri Pirko .procname = name, \ 36461f9248e5SJiri Pirko .data = NEIGH_PARMS_DATA_OFFSET(NEIGH_VAR_ ## data_attr), \ 36471f9248e5SJiri Pirko .maxlen = sizeof(int), \ 36481f9248e5SJiri Pirko .mode = mval, \ 36491f9248e5SJiri Pirko .proc_handler = proc, \ 36501f9248e5SJiri Pirko } 36511f9248e5SJiri Pirko 36521f9248e5SJiri Pirko #define NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(attr, name) \ 36531f9248e5SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_zero_intmax) 36541f9248e5SJiri Pirko 36551f9248e5SJiri Pirko #define NEIGH_SYSCTL_JIFFIES_ENTRY(attr, name) \ 3656cb5b09c1SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_jiffies) 36571f9248e5SJiri Pirko 36581f9248e5SJiri Pirko #define NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(attr, name) \ 3659cb5b09c1SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_userhz_jiffies) 36601f9248e5SJiri Pirko 36611f9248e5SJiri Pirko #define NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(attr, data_attr, name) \ 3662cb5b09c1SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_ms_jiffies) 36631f9248e5SJiri Pirko 36641f9248e5SJiri Pirko #define NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(attr, data_attr, name) \ 3665cb5b09c1SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_unres_qlen) 366654716e3bSEric W. Biederman 36671da177e4SLinus Torvalds static struct neigh_sysctl_table { 36681da177e4SLinus Torvalds struct ctl_table_header *sysctl_header; 36698b5c171bSEric Dumazet struct ctl_table neigh_vars[NEIGH_VAR_MAX + 1]; 3670ab32ea5dSBrian Haley } neigh_sysctl_template __read_mostly = { 36711da177e4SLinus Torvalds .neigh_vars = { 36721f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(MCAST_PROBES, "mcast_solicit"), 36731f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(UCAST_PROBES, "ucast_solicit"), 36741f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(APP_PROBES, "app_solicit"), 36758da86466SYOSHIFUJI Hideaki/吉藤英明 NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(MCAST_REPROBES, "mcast_resolicit"), 36761f9248e5SJiri Pirko NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(RETRANS_TIME, "retrans_time"), 36771f9248e5SJiri Pirko NEIGH_SYSCTL_JIFFIES_ENTRY(BASE_REACHABLE_TIME, "base_reachable_time"), 36781f9248e5SJiri Pirko NEIGH_SYSCTL_JIFFIES_ENTRY(DELAY_PROBE_TIME, "delay_first_probe_time"), 36791f9248e5SJiri Pirko NEIGH_SYSCTL_JIFFIES_ENTRY(GC_STALETIME, "gc_stale_time"), 36801f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(QUEUE_LEN_BYTES, "unres_qlen_bytes"), 36811f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(PROXY_QLEN, "proxy_qlen"), 36821f9248e5SJiri Pirko NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(ANYCAST_DELAY, "anycast_delay"), 36831f9248e5SJiri Pirko NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(PROXY_DELAY, "proxy_delay"), 36841f9248e5SJiri Pirko NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(LOCKTIME, "locktime"), 36851f9248e5SJiri Pirko NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(QUEUE_LEN, QUEUE_LEN_BYTES, "unres_qlen"), 36861f9248e5SJiri Pirko NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(RETRANS_TIME_MS, RETRANS_TIME, "retrans_time_ms"), 36871f9248e5SJiri Pirko NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(BASE_REACHABLE_TIME_MS, BASE_REACHABLE_TIME, "base_reachable_time_ms"), 36888b5c171bSEric Dumazet [NEIGH_VAR_GC_INTERVAL] = { 36891da177e4SLinus Torvalds .procname = "gc_interval", 36901da177e4SLinus Torvalds .maxlen = sizeof(int), 36911da177e4SLinus Torvalds .mode = 0644, 36926d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 36931da177e4SLinus Torvalds }, 36948b5c171bSEric Dumazet [NEIGH_VAR_GC_THRESH1] = { 36951da177e4SLinus Torvalds .procname = "gc_thresh1", 36961da177e4SLinus Torvalds .maxlen = sizeof(int), 36971da177e4SLinus Torvalds .mode = 0644, 3698eec4844fSMatteo Croce .extra1 = SYSCTL_ZERO, 3699eec4844fSMatteo Croce .extra2 = SYSCTL_INT_MAX, 3700555445cdSFrancesco Fusco .proc_handler = proc_dointvec_minmax, 37011da177e4SLinus Torvalds }, 37028b5c171bSEric Dumazet [NEIGH_VAR_GC_THRESH2] = { 37031da177e4SLinus Torvalds .procname = "gc_thresh2", 37041da177e4SLinus Torvalds .maxlen = sizeof(int), 37051da177e4SLinus Torvalds .mode = 0644, 3706eec4844fSMatteo Croce .extra1 = SYSCTL_ZERO, 3707eec4844fSMatteo Croce .extra2 = SYSCTL_INT_MAX, 3708555445cdSFrancesco Fusco .proc_handler = proc_dointvec_minmax, 37091da177e4SLinus Torvalds }, 37108b5c171bSEric Dumazet [NEIGH_VAR_GC_THRESH3] = { 37111da177e4SLinus Torvalds .procname = "gc_thresh3", 37121da177e4SLinus Torvalds .maxlen = sizeof(int), 37131da177e4SLinus Torvalds .mode = 0644, 3714eec4844fSMatteo Croce .extra1 = SYSCTL_ZERO, 3715eec4844fSMatteo Croce .extra2 = SYSCTL_INT_MAX, 3716555445cdSFrancesco Fusco .proc_handler = proc_dointvec_minmax, 37171da177e4SLinus Torvalds }, 3718c3bac5a7SPavel Emelyanov {}, 37191da177e4SLinus Torvalds }, 37201da177e4SLinus Torvalds }; 37211da177e4SLinus Torvalds 37221da177e4SLinus Torvalds int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, 372373af614aSJiri Pirko proc_handler *handler) 37241da177e4SLinus Torvalds { 37251f9248e5SJiri Pirko int i; 37263c607bbbSPavel Emelyanov struct neigh_sysctl_table *t; 37271f9248e5SJiri Pirko const char *dev_name_source; 37288f40a1f9SEric W. Biederman char neigh_path[ sizeof("net//neigh/") + IFNAMSIZ + IFNAMSIZ ]; 372973af614aSJiri Pirko char *p_name; 37301da177e4SLinus Torvalds 3731425b9c7fSVasily Averin t = kmemdup(&neigh_sysctl_template, sizeof(*t), GFP_KERNEL_ACCOUNT); 37321da177e4SLinus Torvalds if (!t) 37333c607bbbSPavel Emelyanov goto err; 37343c607bbbSPavel Emelyanov 3735b194c1f1SJiri Pirko for (i = 0; i < NEIGH_VAR_GC_INTERVAL; i++) { 37361f9248e5SJiri Pirko t->neigh_vars[i].data += (long) p; 3737cb5b09c1SJiri Pirko t->neigh_vars[i].extra1 = dev; 37381d4c8c29SJiri Pirko t->neigh_vars[i].extra2 = p; 3739cb5b09c1SJiri Pirko } 37401da177e4SLinus Torvalds 37411da177e4SLinus Torvalds if (dev) { 37421da177e4SLinus Torvalds dev_name_source = dev->name; 3743d12af679SEric W. Biederman /* Terminate the table early */ 37448b5c171bSEric Dumazet memset(&t->neigh_vars[NEIGH_VAR_GC_INTERVAL], 0, 37458b5c171bSEric Dumazet sizeof(t->neigh_vars[NEIGH_VAR_GC_INTERVAL])); 37461da177e4SLinus Torvalds } else { 37479ecf07a1SMathias Krause struct neigh_table *tbl = p->tbl; 37488f40a1f9SEric W. Biederman dev_name_source = "default"; 37499ecf07a1SMathias Krause t->neigh_vars[NEIGH_VAR_GC_INTERVAL].data = &tbl->gc_interval; 37509ecf07a1SMathias Krause t->neigh_vars[NEIGH_VAR_GC_THRESH1].data = &tbl->gc_thresh1; 37519ecf07a1SMathias Krause t->neigh_vars[NEIGH_VAR_GC_THRESH2].data = &tbl->gc_thresh2; 37529ecf07a1SMathias Krause t->neigh_vars[NEIGH_VAR_GC_THRESH3].data = &tbl->gc_thresh3; 37531da177e4SLinus Torvalds } 37541da177e4SLinus Torvalds 3755f8572d8fSEric W. Biederman if (handler) { 37561da177e4SLinus Torvalds /* RetransTime */ 37578b5c171bSEric Dumazet t->neigh_vars[NEIGH_VAR_RETRANS_TIME].proc_handler = handler; 37581da177e4SLinus Torvalds /* ReachableTime */ 37598b5c171bSEric Dumazet t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].proc_handler = handler; 37601da177e4SLinus Torvalds /* RetransTime (in milliseconds)*/ 37618b5c171bSEric Dumazet t->neigh_vars[NEIGH_VAR_RETRANS_TIME_MS].proc_handler = handler; 37621da177e4SLinus Torvalds /* ReachableTime (in milliseconds) */ 37638b5c171bSEric Dumazet t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].proc_handler = handler; 37644bf6980dSJean-Francois Remy } else { 37654bf6980dSJean-Francois Remy /* Those handlers will update p->reachable_time after 37664bf6980dSJean-Francois Remy * base_reachable_time(_ms) is set to ensure the new timer starts being 37674bf6980dSJean-Francois Remy * applied after the next neighbour update instead of waiting for 37684bf6980dSJean-Francois Remy * neigh_periodic_work to update its value (can be multiple minutes) 37694bf6980dSJean-Francois Remy * So any handler that replaces them should do this as well 37704bf6980dSJean-Francois Remy */ 37714bf6980dSJean-Francois Remy /* ReachableTime */ 37724bf6980dSJean-Francois Remy t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].proc_handler = 37734bf6980dSJean-Francois Remy neigh_proc_base_reachable_time; 37744bf6980dSJean-Francois Remy /* ReachableTime (in milliseconds) */ 37754bf6980dSJean-Francois Remy t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].proc_handler = 37764bf6980dSJean-Francois Remy neigh_proc_base_reachable_time; 37771da177e4SLinus Torvalds } 37781da177e4SLinus Torvalds 377973af614aSJiri Pirko switch (neigh_parms_family(p)) { 378073af614aSJiri Pirko case AF_INET: 378173af614aSJiri Pirko p_name = "ipv4"; 378273af614aSJiri Pirko break; 378373af614aSJiri Pirko case AF_INET6: 378473af614aSJiri Pirko p_name = "ipv6"; 378573af614aSJiri Pirko break; 378673af614aSJiri Pirko default: 378773af614aSJiri Pirko BUG(); 378873af614aSJiri Pirko } 378973af614aSJiri Pirko 37908f40a1f9SEric W. Biederman snprintf(neigh_path, sizeof(neigh_path), "net/%s/neigh/%s", 37918f40a1f9SEric W. Biederman p_name, dev_name_source); 37924ab438fcSDenis V. Lunev t->sysctl_header = 37938f40a1f9SEric W. Biederman register_net_sysctl(neigh_parms_net(p), neigh_path, t->neigh_vars); 37943c607bbbSPavel Emelyanov if (!t->sysctl_header) 37958f40a1f9SEric W. Biederman goto free; 37963c607bbbSPavel Emelyanov 37971da177e4SLinus Torvalds p->sysctl_table = t; 37981da177e4SLinus Torvalds return 0; 37991da177e4SLinus Torvalds 38001da177e4SLinus Torvalds free: 38011da177e4SLinus Torvalds kfree(t); 38023c607bbbSPavel Emelyanov err: 38033c607bbbSPavel Emelyanov return -ENOBUFS; 38041da177e4SLinus Torvalds } 38050a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_sysctl_register); 38061da177e4SLinus Torvalds 38071da177e4SLinus Torvalds void neigh_sysctl_unregister(struct neigh_parms *p) 38081da177e4SLinus Torvalds { 38091da177e4SLinus Torvalds if (p->sysctl_table) { 38101da177e4SLinus Torvalds struct neigh_sysctl_table *t = p->sysctl_table; 38111da177e4SLinus Torvalds p->sysctl_table = NULL; 38125dd3df10SEric W. Biederman unregister_net_sysctl_table(t->sysctl_header); 38131da177e4SLinus Torvalds kfree(t); 38141da177e4SLinus Torvalds } 38151da177e4SLinus Torvalds } 38160a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_sysctl_unregister); 38171da177e4SLinus Torvalds 38181da177e4SLinus Torvalds #endif /* CONFIG_SYSCTL */ 38191da177e4SLinus Torvalds 3820c8822a4eSThomas Graf static int __init neigh_init(void) 3821c8822a4eSThomas Graf { 3822b97bac64SFlorian Westphal rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL, 0); 3823b97bac64SFlorian Westphal rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL, 0); 382482cbb5c6SRoopa Prabhu rtnl_register(PF_UNSPEC, RTM_GETNEIGH, neigh_get, neigh_dump_info, 0); 3825c8822a4eSThomas Graf 3826c7ac8679SGreg Rose rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info, 3827b97bac64SFlorian Westphal 0); 3828b97bac64SFlorian Westphal rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL, 0); 3829c8822a4eSThomas Graf 3830c8822a4eSThomas Graf return 0; 3831c8822a4eSThomas Graf } 3832c8822a4eSThomas Graf 3833c8822a4eSThomas Graf subsys_initcall(neigh_init); 3834