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 44d5d427cdSJoe Perches #define DEBUG 451da177e4SLinus Torvalds #define NEIGH_DEBUG 1 46d5d427cdSJoe Perches #define neigh_dbg(level, fmt, ...) \ 47d5d427cdSJoe Perches do { \ 48d5d427cdSJoe Perches if (level <= NEIGH_DEBUG) \ 49d5d427cdSJoe Perches pr_debug(fmt, ##__VA_ARGS__); \ 50d5d427cdSJoe Perches } while (0) 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds #define PNEIGH_HASHMASK 0xF 531da177e4SLinus Torvalds 54e99e88a9SKees Cook static void neigh_timer_handler(struct timer_list *t); 557b8f7a40SRoopa Prabhu static void __neigh_notify(struct neighbour *n, int type, int flags, 567b8f7a40SRoopa Prabhu u32 pid); 577b8f7a40SRoopa Prabhu static void neigh_update_notify(struct neighbour *neigh, u32 nlmsg_pid); 5853b76cdfSWolfgang Bumiller static int pneigh_ifdown_and_unlock(struct neigh_table *tbl, 5953b76cdfSWolfgang Bumiller struct net_device *dev); 601da177e4SLinus Torvalds 6145fc3b11SAmos Waterland #ifdef CONFIG_PROC_FS 6271a5053aSChristoph Hellwig static const struct seq_operations neigh_stat_seq_ops; 6345fc3b11SAmos Waterland #endif 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds /* 661da177e4SLinus Torvalds Neighbour hash table buckets are protected with rwlock tbl->lock. 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds - All the scans/updates to hash buckets MUST be made under this lock. 691da177e4SLinus Torvalds - NOTHING clever should be made under this lock: no callbacks 701da177e4SLinus Torvalds to protocol backends, no attempts to send something to network. 711da177e4SLinus Torvalds It will result in deadlocks, if backend/driver wants to use neighbour 721da177e4SLinus Torvalds cache. 731da177e4SLinus Torvalds - If the entry requires some non-trivial actions, increase 741da177e4SLinus Torvalds its reference count and release table lock. 751da177e4SLinus Torvalds 761da177e4SLinus Torvalds Neighbour entries are protected: 771da177e4SLinus Torvalds - with reference count. 781da177e4SLinus Torvalds - with rwlock neigh->lock 791da177e4SLinus Torvalds 801da177e4SLinus Torvalds Reference count prevents destruction. 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds neigh->lock mainly serializes ll address data and its validity state. 831da177e4SLinus Torvalds However, the same lock is used to protect another entry fields: 841da177e4SLinus Torvalds - timer 851da177e4SLinus Torvalds - resolution queue 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds Again, nothing clever shall be made under neigh->lock, 881da177e4SLinus Torvalds the most complicated procedure, which we allow is dev->hard_header. 891da177e4SLinus Torvalds It is supposed, that dev->hard_header is simplistic and does 901da177e4SLinus Torvalds not make callbacks to neighbour tables. 911da177e4SLinus Torvalds */ 921da177e4SLinus Torvalds 938f40b161SDavid S. Miller static int neigh_blackhole(struct neighbour *neigh, struct sk_buff *skb) 941da177e4SLinus Torvalds { 951da177e4SLinus Torvalds kfree_skb(skb); 961da177e4SLinus Torvalds return -ENETDOWN; 971da177e4SLinus Torvalds } 981da177e4SLinus Torvalds 994f494554SThomas Graf static void neigh_cleanup_and_release(struct neighbour *neigh) 1004f494554SThomas Graf { 1014f494554SThomas Graf if (neigh->parms->neigh_cleanup) 1024f494554SThomas Graf neigh->parms->neigh_cleanup(neigh); 1034f494554SThomas Graf 10456dd18a4SRoopa Prabhu trace_neigh_cleanup_and_release(neigh, 0); 1057b8f7a40SRoopa Prabhu __neigh_notify(neigh, RTM_DELNEIGH, 0, 0); 10653f800e3SIdo Schimmel call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh); 1074f494554SThomas Graf neigh_release(neigh); 1084f494554SThomas Graf } 1094f494554SThomas Graf 1101da177e4SLinus Torvalds /* 1111da177e4SLinus Torvalds * It is random distribution in the interval (1/2)*base...(3/2)*base. 1121da177e4SLinus Torvalds * It corresponds to default IPv6 settings and is not overridable, 1131da177e4SLinus Torvalds * because it is really reasonable choice. 1141da177e4SLinus Torvalds */ 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds unsigned long neigh_rand_reach_time(unsigned long base) 1171da177e4SLinus Torvalds { 11863862b5bSAruna-Hewapathirane return base ? (prandom_u32() % base) + (base >> 1) : 0; 1191da177e4SLinus Torvalds } 1200a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_rand_reach_time); 1211da177e4SLinus Torvalds 12258956317SDavid Ahern static void neigh_mark_dead(struct neighbour *n) 12358956317SDavid Ahern { 12458956317SDavid Ahern n->dead = 1; 12558956317SDavid Ahern if (!list_empty(&n->gc_list)) { 12658956317SDavid Ahern list_del_init(&n->gc_list); 12758956317SDavid Ahern atomic_dec(&n->tbl->gc_entries); 12858956317SDavid Ahern } 12958956317SDavid Ahern } 13058956317SDavid Ahern 1319c29a2f5SDavid Ahern static void neigh_update_gc_list(struct neighbour *n) 13258956317SDavid Ahern { 133e997f8a2SDavid Ahern bool on_gc_list, exempt_from_gc; 13458956317SDavid Ahern 1359c29a2f5SDavid Ahern write_lock_bh(&n->tbl->lock); 1369c29a2f5SDavid Ahern write_lock(&n->lock); 13758956317SDavid Ahern 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 } 1539c29a2f5SDavid Ahern 1549c29a2f5SDavid Ahern write_unlock(&n->lock); 1559c29a2f5SDavid Ahern write_unlock_bh(&n->tbl->lock); 15658956317SDavid Ahern } 1571da177e4SLinus Torvalds 158e997f8a2SDavid Ahern static bool neigh_update_ext_learned(struct neighbour *neigh, u32 flags, 159526f1b58SDavid Ahern int *notify) 160526f1b58SDavid Ahern { 161e997f8a2SDavid Ahern bool rc = false; 162526f1b58SDavid Ahern u8 ndm_flags; 163526f1b58SDavid Ahern 164526f1b58SDavid Ahern if (!(flags & NEIGH_UPDATE_F_ADMIN)) 165e997f8a2SDavid Ahern return rc; 166526f1b58SDavid Ahern 167526f1b58SDavid Ahern ndm_flags = (flags & NEIGH_UPDATE_F_EXT_LEARNED) ? NTF_EXT_LEARNED : 0; 168526f1b58SDavid Ahern if ((neigh->flags ^ ndm_flags) & NTF_EXT_LEARNED) { 169526f1b58SDavid Ahern if (ndm_flags & NTF_EXT_LEARNED) 170526f1b58SDavid Ahern neigh->flags |= NTF_EXT_LEARNED; 171526f1b58SDavid Ahern else 172526f1b58SDavid Ahern neigh->flags &= ~NTF_EXT_LEARNED; 173e997f8a2SDavid Ahern rc = true; 174526f1b58SDavid Ahern *notify = 1; 175526f1b58SDavid Ahern } 176e997f8a2SDavid Ahern 177e997f8a2SDavid Ahern return rc; 178526f1b58SDavid Ahern } 179526f1b58SDavid Ahern 1807e6f182bSDavid Ahern static bool neigh_del(struct neighbour *n, struct neighbour __rcu **np, 1817e6f182bSDavid Ahern struct neigh_table *tbl) 1825071034eSSowmini Varadhan { 1835071034eSSowmini Varadhan bool retval = false; 1845071034eSSowmini Varadhan 1855071034eSSowmini Varadhan write_lock(&n->lock); 1867e6f182bSDavid Ahern if (refcount_read(&n->refcnt) == 1) { 1875071034eSSowmini Varadhan struct neighbour *neigh; 1885071034eSSowmini Varadhan 1895071034eSSowmini Varadhan neigh = rcu_dereference_protected(n->next, 1905071034eSSowmini Varadhan lockdep_is_held(&tbl->lock)); 1915071034eSSowmini Varadhan rcu_assign_pointer(*np, neigh); 19258956317SDavid Ahern neigh_mark_dead(n); 1935071034eSSowmini Varadhan retval = true; 1945071034eSSowmini Varadhan } 1955071034eSSowmini Varadhan write_unlock(&n->lock); 1965071034eSSowmini Varadhan if (retval) 1975071034eSSowmini Varadhan neigh_cleanup_and_release(n); 1985071034eSSowmini Varadhan return retval; 1995071034eSSowmini Varadhan } 2005071034eSSowmini Varadhan 2015071034eSSowmini Varadhan bool neigh_remove_one(struct neighbour *ndel, struct neigh_table *tbl) 2025071034eSSowmini Varadhan { 2035071034eSSowmini Varadhan struct neigh_hash_table *nht; 2045071034eSSowmini Varadhan void *pkey = ndel->primary_key; 2055071034eSSowmini Varadhan u32 hash_val; 2065071034eSSowmini Varadhan struct neighbour *n; 2075071034eSSowmini Varadhan struct neighbour __rcu **np; 2085071034eSSowmini Varadhan 2095071034eSSowmini Varadhan nht = rcu_dereference_protected(tbl->nht, 2105071034eSSowmini Varadhan lockdep_is_held(&tbl->lock)); 2115071034eSSowmini Varadhan hash_val = tbl->hash(pkey, ndel->dev, nht->hash_rnd); 2125071034eSSowmini Varadhan hash_val = hash_val >> (32 - nht->hash_shift); 2135071034eSSowmini Varadhan 2145071034eSSowmini Varadhan np = &nht->hash_buckets[hash_val]; 2155071034eSSowmini Varadhan while ((n = rcu_dereference_protected(*np, 2165071034eSSowmini Varadhan lockdep_is_held(&tbl->lock)))) { 2175071034eSSowmini Varadhan if (n == ndel) 2187e6f182bSDavid Ahern return neigh_del(n, np, tbl); 2195071034eSSowmini Varadhan np = &n->next; 2205071034eSSowmini Varadhan } 2215071034eSSowmini Varadhan return false; 2225071034eSSowmini Varadhan } 2235071034eSSowmini Varadhan 2241da177e4SLinus Torvalds static int neigh_forced_gc(struct neigh_table *tbl) 2251da177e4SLinus Torvalds { 22658956317SDavid Ahern int max_clean = atomic_read(&tbl->gc_entries) - tbl->gc_thresh2; 22758956317SDavid Ahern unsigned long tref = jiffies - 5 * HZ; 22858956317SDavid Ahern struct neighbour *n, *tmp; 2291da177e4SLinus Torvalds int shrunk = 0; 2301da177e4SLinus Torvalds 2311da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, forced_gc_runs); 2321da177e4SLinus Torvalds 2331da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 2341da177e4SLinus Torvalds 23558956317SDavid Ahern list_for_each_entry_safe(n, tmp, &tbl->gc_list, gc_list) { 23658956317SDavid Ahern if (refcount_read(&n->refcnt) == 1) { 23758956317SDavid Ahern bool remove = false; 23858956317SDavid Ahern 23958956317SDavid Ahern write_lock(&n->lock); 240758a7f0bSDavid Ahern if ((n->nud_state == NUD_FAILED) || 241e997f8a2SDavid Ahern time_after(tref, n->updated)) 24258956317SDavid Ahern remove = true; 24358956317SDavid Ahern write_unlock(&n->lock); 24458956317SDavid Ahern 24558956317SDavid Ahern if (remove && neigh_remove_one(n, tbl)) 24658956317SDavid Ahern shrunk++; 24758956317SDavid Ahern if (shrunk >= max_clean) 24858956317SDavid Ahern break; 2491da177e4SLinus Torvalds } 2501da177e4SLinus Torvalds } 2511da177e4SLinus Torvalds 2521da177e4SLinus Torvalds tbl->last_flush = jiffies; 2531da177e4SLinus Torvalds 2541da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 2551da177e4SLinus Torvalds 2561da177e4SLinus Torvalds return shrunk; 2571da177e4SLinus Torvalds } 2581da177e4SLinus Torvalds 259a43d8994SPavel Emelyanov static void neigh_add_timer(struct neighbour *n, unsigned long when) 260a43d8994SPavel Emelyanov { 261a43d8994SPavel Emelyanov neigh_hold(n); 262a43d8994SPavel Emelyanov if (unlikely(mod_timer(&n->timer, when))) { 263a43d8994SPavel Emelyanov printk("NEIGH: BUG, double timer add, state is %x\n", 264a43d8994SPavel Emelyanov n->nud_state); 265a43d8994SPavel Emelyanov dump_stack(); 266a43d8994SPavel Emelyanov } 267a43d8994SPavel Emelyanov } 268a43d8994SPavel Emelyanov 2691da177e4SLinus Torvalds static int neigh_del_timer(struct neighbour *n) 2701da177e4SLinus Torvalds { 2711da177e4SLinus Torvalds if ((n->nud_state & NUD_IN_TIMER) && 2721da177e4SLinus Torvalds del_timer(&n->timer)) { 2731da177e4SLinus Torvalds neigh_release(n); 2741da177e4SLinus Torvalds return 1; 2751da177e4SLinus Torvalds } 2761da177e4SLinus Torvalds return 0; 2771da177e4SLinus Torvalds } 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds static void pneigh_queue_purge(struct sk_buff_head *list) 2801da177e4SLinus Torvalds { 2811da177e4SLinus Torvalds struct sk_buff *skb; 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds while ((skb = skb_dequeue(list)) != NULL) { 2841da177e4SLinus Torvalds dev_put(skb->dev); 2851da177e4SLinus Torvalds kfree_skb(skb); 2861da177e4SLinus Torvalds } 2871da177e4SLinus Torvalds } 2881da177e4SLinus Torvalds 289859bd2efSDavid Ahern static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev, 290859bd2efSDavid Ahern bool skip_perm) 2911da177e4SLinus Torvalds { 2921da177e4SLinus Torvalds int i; 293d6bf7817SEric Dumazet struct neigh_hash_table *nht; 2941da177e4SLinus Torvalds 295d6bf7817SEric Dumazet nht = rcu_dereference_protected(tbl->nht, 296d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 297d6bf7817SEric Dumazet 298cd089336SDavid S. Miller for (i = 0; i < (1 << nht->hash_shift); i++) { 299767e97e1SEric Dumazet struct neighbour *n; 300767e97e1SEric Dumazet struct neighbour __rcu **np = &nht->hash_buckets[i]; 3011da177e4SLinus Torvalds 302767e97e1SEric Dumazet while ((n = rcu_dereference_protected(*np, 303767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))) != NULL) { 3041da177e4SLinus Torvalds if (dev && n->dev != dev) { 3051da177e4SLinus Torvalds np = &n->next; 3061da177e4SLinus Torvalds continue; 3071da177e4SLinus Torvalds } 308859bd2efSDavid Ahern if (skip_perm && n->nud_state & NUD_PERMANENT) { 309859bd2efSDavid Ahern np = &n->next; 310859bd2efSDavid Ahern continue; 311859bd2efSDavid Ahern } 312767e97e1SEric Dumazet rcu_assign_pointer(*np, 313767e97e1SEric Dumazet rcu_dereference_protected(n->next, 314767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))); 3151da177e4SLinus Torvalds write_lock(&n->lock); 3161da177e4SLinus Torvalds neigh_del_timer(n); 31758956317SDavid Ahern neigh_mark_dead(n); 3189f237430SReshetova, Elena if (refcount_read(&n->refcnt) != 1) { 3191da177e4SLinus Torvalds /* The most unpleasant situation. 3201da177e4SLinus Torvalds We must destroy neighbour entry, 3211da177e4SLinus Torvalds but someone still uses it. 3221da177e4SLinus Torvalds 3231da177e4SLinus Torvalds The destroy will be delayed until 3241da177e4SLinus Torvalds the last user releases us, but 3251da177e4SLinus Torvalds we must kill timers etc. and move 3261da177e4SLinus Torvalds it to safe state. 3271da177e4SLinus Torvalds */ 328c9ab4d85SEric Dumazet __skb_queue_purge(&n->arp_queue); 3298b5c171bSEric Dumazet n->arp_queue_len_bytes = 0; 3301da177e4SLinus Torvalds n->output = neigh_blackhole; 3311da177e4SLinus Torvalds if (n->nud_state & NUD_VALID) 3321da177e4SLinus Torvalds n->nud_state = NUD_NOARP; 3331da177e4SLinus Torvalds else 3341da177e4SLinus Torvalds n->nud_state = NUD_NONE; 335d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is stray\n", n); 3361da177e4SLinus Torvalds } 3371da177e4SLinus Torvalds write_unlock(&n->lock); 3384f494554SThomas Graf neigh_cleanup_and_release(n); 3391da177e4SLinus Torvalds } 3401da177e4SLinus Torvalds } 34149636bb1SHerbert Xu } 3421da177e4SLinus Torvalds 34349636bb1SHerbert Xu void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev) 34449636bb1SHerbert Xu { 34549636bb1SHerbert Xu write_lock_bh(&tbl->lock); 346859bd2efSDavid Ahern neigh_flush_dev(tbl, dev, false); 34749636bb1SHerbert Xu write_unlock_bh(&tbl->lock); 34849636bb1SHerbert Xu } 3490a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_changeaddr); 35049636bb1SHerbert Xu 351859bd2efSDavid Ahern static int __neigh_ifdown(struct neigh_table *tbl, struct net_device *dev, 352859bd2efSDavid Ahern bool skip_perm) 35349636bb1SHerbert Xu { 35449636bb1SHerbert Xu write_lock_bh(&tbl->lock); 355859bd2efSDavid Ahern neigh_flush_dev(tbl, dev, skip_perm); 35653b76cdfSWolfgang Bumiller pneigh_ifdown_and_unlock(tbl, dev); 3571da177e4SLinus Torvalds 3581da177e4SLinus Torvalds del_timer_sync(&tbl->proxy_timer); 3591da177e4SLinus Torvalds pneigh_queue_purge(&tbl->proxy_queue); 3601da177e4SLinus Torvalds return 0; 3611da177e4SLinus Torvalds } 362859bd2efSDavid Ahern 363859bd2efSDavid Ahern int neigh_carrier_down(struct neigh_table *tbl, struct net_device *dev) 364859bd2efSDavid Ahern { 365859bd2efSDavid Ahern __neigh_ifdown(tbl, dev, true); 366859bd2efSDavid Ahern return 0; 367859bd2efSDavid Ahern } 368859bd2efSDavid Ahern EXPORT_SYMBOL(neigh_carrier_down); 369859bd2efSDavid Ahern 370859bd2efSDavid Ahern int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev) 371859bd2efSDavid Ahern { 372859bd2efSDavid Ahern __neigh_ifdown(tbl, dev, false); 373859bd2efSDavid Ahern return 0; 374859bd2efSDavid Ahern } 3750a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_ifdown); 3761da177e4SLinus Torvalds 37758956317SDavid Ahern static struct neighbour *neigh_alloc(struct neigh_table *tbl, 37858956317SDavid Ahern struct net_device *dev, 379e997f8a2SDavid Ahern bool exempt_from_gc) 3801da177e4SLinus Torvalds { 3811da177e4SLinus Torvalds struct neighbour *n = NULL; 3821da177e4SLinus Torvalds unsigned long now = jiffies; 3831da177e4SLinus Torvalds int entries; 3841da177e4SLinus Torvalds 385e997f8a2SDavid Ahern if (exempt_from_gc) 38658956317SDavid Ahern goto do_alloc; 38758956317SDavid Ahern 38858956317SDavid Ahern entries = atomic_inc_return(&tbl->gc_entries) - 1; 3891da177e4SLinus Torvalds if (entries >= tbl->gc_thresh3 || 3901da177e4SLinus Torvalds (entries >= tbl->gc_thresh2 && 3911da177e4SLinus Torvalds time_after(now, tbl->last_flush + 5 * HZ))) { 3921da177e4SLinus Torvalds if (!neigh_forced_gc(tbl) && 393fb811395SRick Jones entries >= tbl->gc_thresh3) { 394fb811395SRick Jones net_info_ratelimited("%s: neighbor table overflow!\n", 395fb811395SRick Jones tbl->id); 396fb811395SRick Jones NEIGH_CACHE_STAT_INC(tbl, table_fulls); 3971da177e4SLinus Torvalds goto out_entries; 3981da177e4SLinus Torvalds } 399fb811395SRick Jones } 4001da177e4SLinus Torvalds 40158956317SDavid Ahern do_alloc: 40208433effSYOSHIFUJI Hideaki / 吉藤英明 n = kzalloc(tbl->entry_size + dev->neigh_priv_len, GFP_ATOMIC); 4031da177e4SLinus Torvalds if (!n) 4041da177e4SLinus Torvalds goto out_entries; 4051da177e4SLinus Torvalds 406c9ab4d85SEric Dumazet __skb_queue_head_init(&n->arp_queue); 4071da177e4SLinus Torvalds rwlock_init(&n->lock); 4080ed8ddf4SEric Dumazet seqlock_init(&n->ha_lock); 4091da177e4SLinus Torvalds n->updated = n->used = now; 4101da177e4SLinus Torvalds n->nud_state = NUD_NONE; 4111da177e4SLinus Torvalds n->output = neigh_blackhole; 412f6b72b62SDavid S. Miller seqlock_init(&n->hh.hh_lock); 4131da177e4SLinus Torvalds n->parms = neigh_parms_clone(&tbl->parms); 414e99e88a9SKees Cook timer_setup(&n->timer, neigh_timer_handler, 0); 4151da177e4SLinus Torvalds 4161da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, allocs); 4171da177e4SLinus Torvalds n->tbl = tbl; 4189f237430SReshetova, Elena refcount_set(&n->refcnt, 1); 4191da177e4SLinus Torvalds n->dead = 1; 42058956317SDavid Ahern INIT_LIST_HEAD(&n->gc_list); 42158956317SDavid Ahern 42258956317SDavid Ahern atomic_inc(&tbl->entries); 4231da177e4SLinus Torvalds out: 4241da177e4SLinus Torvalds return n; 4251da177e4SLinus Torvalds 4261da177e4SLinus Torvalds out_entries: 427e997f8a2SDavid Ahern if (!exempt_from_gc) 42858956317SDavid Ahern atomic_dec(&tbl->gc_entries); 4291da177e4SLinus Torvalds goto out; 4301da177e4SLinus Torvalds } 4311da177e4SLinus Torvalds 4322c2aba6cSDavid S. Miller static void neigh_get_hash_rnd(u32 *x) 4332c2aba6cSDavid S. Miller { 434b3d0f789SJason A. Donenfeld *x = get_random_u32() | 1; 4352c2aba6cSDavid S. Miller } 4362c2aba6cSDavid S. Miller 437cd089336SDavid S. Miller static struct neigh_hash_table *neigh_hash_alloc(unsigned int shift) 4381da177e4SLinus Torvalds { 439cd089336SDavid S. Miller size_t size = (1 << shift) * sizeof(struct neighbour *); 440d6bf7817SEric Dumazet struct neigh_hash_table *ret; 4416193d2beSEric Dumazet struct neighbour __rcu **buckets; 4422c2aba6cSDavid S. Miller int i; 4431da177e4SLinus Torvalds 444d6bf7817SEric Dumazet ret = kmalloc(sizeof(*ret), GFP_ATOMIC); 445d6bf7817SEric Dumazet if (!ret) 446d6bf7817SEric Dumazet return NULL; 44785704cb8SKonstantin Khlebnikov if (size <= PAGE_SIZE) { 448d6bf7817SEric Dumazet buckets = kzalloc(size, GFP_ATOMIC); 44985704cb8SKonstantin Khlebnikov } else { 4506193d2beSEric Dumazet buckets = (struct neighbour __rcu **) 451d6bf7817SEric Dumazet __get_free_pages(GFP_ATOMIC | __GFP_ZERO, 452d6bf7817SEric Dumazet get_order(size)); 45301b833abSKonstantin Khlebnikov kmemleak_alloc(buckets, size, 1, GFP_ATOMIC); 45485704cb8SKonstantin Khlebnikov } 455d6bf7817SEric Dumazet if (!buckets) { 456d6bf7817SEric Dumazet kfree(ret); 457d6bf7817SEric Dumazet return NULL; 4581da177e4SLinus Torvalds } 4596193d2beSEric Dumazet ret->hash_buckets = buckets; 460cd089336SDavid S. Miller ret->hash_shift = shift; 4612c2aba6cSDavid S. Miller for (i = 0; i < NEIGH_NUM_HASH_RND; i++) 4622c2aba6cSDavid S. Miller neigh_get_hash_rnd(&ret->hash_rnd[i]); 4631da177e4SLinus Torvalds return ret; 4641da177e4SLinus Torvalds } 4651da177e4SLinus Torvalds 466d6bf7817SEric Dumazet static void neigh_hash_free_rcu(struct rcu_head *head) 4671da177e4SLinus Torvalds { 468d6bf7817SEric Dumazet struct neigh_hash_table *nht = container_of(head, 469d6bf7817SEric Dumazet struct neigh_hash_table, 470d6bf7817SEric Dumazet rcu); 471cd089336SDavid S. Miller size_t size = (1 << nht->hash_shift) * sizeof(struct neighbour *); 4726193d2beSEric Dumazet struct neighbour __rcu **buckets = nht->hash_buckets; 4731da177e4SLinus Torvalds 47485704cb8SKonstantin Khlebnikov if (size <= PAGE_SIZE) { 475d6bf7817SEric Dumazet kfree(buckets); 47685704cb8SKonstantin Khlebnikov } else { 47785704cb8SKonstantin Khlebnikov kmemleak_free(buckets); 478d6bf7817SEric Dumazet free_pages((unsigned long)buckets, get_order(size)); 47985704cb8SKonstantin Khlebnikov } 480d6bf7817SEric Dumazet kfree(nht); 4811da177e4SLinus Torvalds } 4821da177e4SLinus Torvalds 483d6bf7817SEric Dumazet static struct neigh_hash_table *neigh_hash_grow(struct neigh_table *tbl, 484cd089336SDavid S. Miller unsigned long new_shift) 4851da177e4SLinus Torvalds { 486d6bf7817SEric Dumazet unsigned int i, hash; 487d6bf7817SEric Dumazet struct neigh_hash_table *new_nht, *old_nht; 4881da177e4SLinus Torvalds 4891da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, hash_grows); 4901da177e4SLinus Torvalds 491d6bf7817SEric Dumazet old_nht = rcu_dereference_protected(tbl->nht, 492d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 493cd089336SDavid S. Miller new_nht = neigh_hash_alloc(new_shift); 494d6bf7817SEric Dumazet if (!new_nht) 495d6bf7817SEric Dumazet return old_nht; 4961da177e4SLinus Torvalds 497cd089336SDavid S. Miller for (i = 0; i < (1 << old_nht->hash_shift); i++) { 4981da177e4SLinus Torvalds struct neighbour *n, *next; 4991da177e4SLinus Torvalds 500767e97e1SEric Dumazet for (n = rcu_dereference_protected(old_nht->hash_buckets[i], 501767e97e1SEric Dumazet lockdep_is_held(&tbl->lock)); 502d6bf7817SEric Dumazet n != NULL; 503d6bf7817SEric Dumazet n = next) { 504d6bf7817SEric Dumazet hash = tbl->hash(n->primary_key, n->dev, 505d6bf7817SEric Dumazet new_nht->hash_rnd); 5061da177e4SLinus Torvalds 507cd089336SDavid S. Miller hash >>= (32 - new_nht->hash_shift); 508767e97e1SEric Dumazet next = rcu_dereference_protected(n->next, 509767e97e1SEric Dumazet lockdep_is_held(&tbl->lock)); 5101da177e4SLinus Torvalds 511767e97e1SEric Dumazet rcu_assign_pointer(n->next, 512767e97e1SEric Dumazet rcu_dereference_protected( 513767e97e1SEric Dumazet new_nht->hash_buckets[hash], 514767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))); 515767e97e1SEric Dumazet rcu_assign_pointer(new_nht->hash_buckets[hash], n); 5161da177e4SLinus Torvalds } 5171da177e4SLinus Torvalds } 5181da177e4SLinus Torvalds 519d6bf7817SEric Dumazet rcu_assign_pointer(tbl->nht, new_nht); 520d6bf7817SEric Dumazet call_rcu(&old_nht->rcu, neigh_hash_free_rcu); 521d6bf7817SEric Dumazet return new_nht; 5221da177e4SLinus Torvalds } 5231da177e4SLinus Torvalds 5241da177e4SLinus Torvalds struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey, 5251da177e4SLinus Torvalds struct net_device *dev) 5261da177e4SLinus Torvalds { 5271da177e4SLinus Torvalds struct neighbour *n; 5281da177e4SLinus Torvalds 5291da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, lookups); 5301da177e4SLinus Torvalds 531d6bf7817SEric Dumazet rcu_read_lock_bh(); 53260395a20SEric W. Biederman n = __neigh_lookup_noref(tbl, pkey, dev); 53360395a20SEric W. Biederman if (n) { 5349f237430SReshetova, Elena if (!refcount_inc_not_zero(&n->refcnt)) 535767e97e1SEric Dumazet n = NULL; 5361da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, hits); 5371da177e4SLinus Torvalds } 538767e97e1SEric Dumazet 539d6bf7817SEric Dumazet rcu_read_unlock_bh(); 5401da177e4SLinus Torvalds return n; 5411da177e4SLinus Torvalds } 5420a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_lookup); 5431da177e4SLinus Torvalds 544426b5303SEric W. Biederman struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net, 545426b5303SEric W. Biederman const void *pkey) 5461da177e4SLinus Torvalds { 5471da177e4SLinus Torvalds struct neighbour *n; 54801ccdf12SAlexey Dobriyan unsigned int key_len = tbl->key_len; 549bc4bf5f3SPavel Emelyanov u32 hash_val; 550d6bf7817SEric Dumazet struct neigh_hash_table *nht; 5511da177e4SLinus Torvalds 5521da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, lookups); 5531da177e4SLinus Torvalds 554d6bf7817SEric Dumazet rcu_read_lock_bh(); 555d6bf7817SEric Dumazet nht = rcu_dereference_bh(tbl->nht); 556cd089336SDavid S. Miller hash_val = tbl->hash(pkey, NULL, nht->hash_rnd) >> (32 - nht->hash_shift); 557767e97e1SEric Dumazet 558767e97e1SEric Dumazet for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]); 559767e97e1SEric Dumazet n != NULL; 560767e97e1SEric Dumazet n = rcu_dereference_bh(n->next)) { 561426b5303SEric W. Biederman if (!memcmp(n->primary_key, pkey, key_len) && 562878628fbSYOSHIFUJI Hideaki net_eq(dev_net(n->dev), net)) { 5639f237430SReshetova, Elena if (!refcount_inc_not_zero(&n->refcnt)) 564767e97e1SEric Dumazet n = NULL; 5651da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, hits); 5661da177e4SLinus Torvalds break; 5671da177e4SLinus Torvalds } 5681da177e4SLinus Torvalds } 569767e97e1SEric Dumazet 570d6bf7817SEric Dumazet rcu_read_unlock_bh(); 5711da177e4SLinus Torvalds return n; 5721da177e4SLinus Torvalds } 5730a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_lookup_nodev); 5741da177e4SLinus Torvalds 57558956317SDavid Ahern static struct neighbour *___neigh_create(struct neigh_table *tbl, 57658956317SDavid Ahern const void *pkey, 57758956317SDavid Ahern struct net_device *dev, 578e997f8a2SDavid Ahern bool exempt_from_gc, bool want_ref) 5791da177e4SLinus Torvalds { 580e997f8a2SDavid Ahern struct neighbour *n1, *rc, *n = neigh_alloc(tbl, dev, exempt_from_gc); 5811da177e4SLinus Torvalds u32 hash_val; 58201ccdf12SAlexey Dobriyan unsigned int key_len = tbl->key_len; 5831da177e4SLinus Torvalds int error; 584d6bf7817SEric Dumazet struct neigh_hash_table *nht; 5851da177e4SLinus Torvalds 5861da177e4SLinus Torvalds if (!n) { 5871da177e4SLinus Torvalds rc = ERR_PTR(-ENOBUFS); 5881da177e4SLinus Torvalds goto out; 5891da177e4SLinus Torvalds } 5901da177e4SLinus Torvalds 5911da177e4SLinus Torvalds memcpy(n->primary_key, pkey, key_len); 5921da177e4SLinus Torvalds n->dev = dev; 5931da177e4SLinus Torvalds dev_hold(dev); 5941da177e4SLinus Torvalds 5951da177e4SLinus Torvalds /* Protocol specific setup. */ 5961da177e4SLinus Torvalds if (tbl->constructor && (error = tbl->constructor(n)) < 0) { 5971da177e4SLinus Torvalds rc = ERR_PTR(error); 5981da177e4SLinus Torvalds goto out_neigh_release; 5991da177e4SLinus Torvalds } 6001da177e4SLinus Torvalds 601da6a8fa0SDavid Miller if (dev->netdev_ops->ndo_neigh_construct) { 602503eebc2SJiri Pirko error = dev->netdev_ops->ndo_neigh_construct(dev, n); 603da6a8fa0SDavid Miller if (error < 0) { 604da6a8fa0SDavid Miller rc = ERR_PTR(error); 605da6a8fa0SDavid Miller goto out_neigh_release; 606da6a8fa0SDavid Miller } 607da6a8fa0SDavid Miller } 608da6a8fa0SDavid Miller 609447f2191SDavid S. Miller /* Device specific setup. */ 610447f2191SDavid S. Miller if (n->parms->neigh_setup && 611447f2191SDavid S. Miller (error = n->parms->neigh_setup(n)) < 0) { 612447f2191SDavid S. Miller rc = ERR_PTR(error); 613447f2191SDavid S. Miller goto out_neigh_release; 614447f2191SDavid S. Miller } 615447f2191SDavid S. Miller 6161f9248e5SJiri Pirko n->confirmed = jiffies - (NEIGH_VAR(n->parms, BASE_REACHABLE_TIME) << 1); 6171da177e4SLinus Torvalds 6181da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 619d6bf7817SEric Dumazet nht = rcu_dereference_protected(tbl->nht, 620d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 6211da177e4SLinus Torvalds 622cd089336SDavid S. Miller if (atomic_read(&tbl->entries) > (1 << nht->hash_shift)) 623cd089336SDavid S. Miller nht = neigh_hash_grow(tbl, nht->hash_shift + 1); 6241da177e4SLinus Torvalds 625096b9854SJim Westfall hash_val = tbl->hash(n->primary_key, dev, nht->hash_rnd) >> (32 - nht->hash_shift); 6261da177e4SLinus Torvalds 6271da177e4SLinus Torvalds if (n->parms->dead) { 6281da177e4SLinus Torvalds rc = ERR_PTR(-EINVAL); 6291da177e4SLinus Torvalds goto out_tbl_unlock; 6301da177e4SLinus Torvalds } 6311da177e4SLinus Torvalds 632767e97e1SEric Dumazet for (n1 = rcu_dereference_protected(nht->hash_buckets[hash_val], 633767e97e1SEric Dumazet lockdep_is_held(&tbl->lock)); 634767e97e1SEric Dumazet n1 != NULL; 635767e97e1SEric Dumazet n1 = rcu_dereference_protected(n1->next, 636767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))) { 637096b9854SJim Westfall if (dev == n1->dev && !memcmp(n1->primary_key, n->primary_key, key_len)) { 638a263b309SDavid S. Miller if (want_ref) 6391da177e4SLinus Torvalds neigh_hold(n1); 6401da177e4SLinus Torvalds rc = n1; 6411da177e4SLinus Torvalds goto out_tbl_unlock; 6421da177e4SLinus Torvalds } 6431da177e4SLinus Torvalds } 6441da177e4SLinus Torvalds 6451da177e4SLinus Torvalds n->dead = 0; 646e997f8a2SDavid Ahern if (!exempt_from_gc) 6478cc196d6SDavid Ahern list_add_tail(&n->gc_list, &n->tbl->gc_list); 6488cc196d6SDavid Ahern 649a263b309SDavid S. Miller if (want_ref) 6501da177e4SLinus Torvalds neigh_hold(n); 651767e97e1SEric Dumazet rcu_assign_pointer(n->next, 652767e97e1SEric Dumazet rcu_dereference_protected(nht->hash_buckets[hash_val], 653767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))); 654767e97e1SEric Dumazet rcu_assign_pointer(nht->hash_buckets[hash_val], n); 6551da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 656d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is created\n", n); 6571da177e4SLinus Torvalds rc = n; 6581da177e4SLinus Torvalds out: 6591da177e4SLinus Torvalds return rc; 6601da177e4SLinus Torvalds out_tbl_unlock: 6611da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 6621da177e4SLinus Torvalds out_neigh_release: 66364c6f4bbSDavid Ahern if (!exempt_from_gc) 66464c6f4bbSDavid Ahern atomic_dec(&tbl->gc_entries); 6651da177e4SLinus Torvalds neigh_release(n); 6661da177e4SLinus Torvalds goto out; 6671da177e4SLinus Torvalds } 66858956317SDavid Ahern 66958956317SDavid Ahern struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey, 67058956317SDavid Ahern struct net_device *dev, bool want_ref) 67158956317SDavid Ahern { 67258956317SDavid Ahern return ___neigh_create(tbl, pkey, dev, false, want_ref); 67358956317SDavid Ahern } 674a263b309SDavid S. Miller EXPORT_SYMBOL(__neigh_create); 6751da177e4SLinus Torvalds 67601ccdf12SAlexey Dobriyan static u32 pneigh_hash(const void *pkey, unsigned int key_len) 677fa86d322SPavel Emelyanov { 678fa86d322SPavel Emelyanov u32 hash_val = *(u32 *)(pkey + key_len - 4); 679fa86d322SPavel Emelyanov hash_val ^= (hash_val >> 16); 680fa86d322SPavel Emelyanov hash_val ^= hash_val >> 8; 681fa86d322SPavel Emelyanov hash_val ^= hash_val >> 4; 682fa86d322SPavel Emelyanov hash_val &= PNEIGH_HASHMASK; 683be01d655SYOSHIFUJI Hideaki return hash_val; 684fa86d322SPavel Emelyanov } 685fa86d322SPavel Emelyanov 686be01d655SYOSHIFUJI Hideaki static struct pneigh_entry *__pneigh_lookup_1(struct pneigh_entry *n, 687be01d655SYOSHIFUJI Hideaki struct net *net, 688be01d655SYOSHIFUJI Hideaki const void *pkey, 68901ccdf12SAlexey Dobriyan unsigned int key_len, 690be01d655SYOSHIFUJI Hideaki struct net_device *dev) 691be01d655SYOSHIFUJI Hideaki { 692be01d655SYOSHIFUJI Hideaki while (n) { 693be01d655SYOSHIFUJI Hideaki if (!memcmp(n->key, pkey, key_len) && 694be01d655SYOSHIFUJI Hideaki net_eq(pneigh_net(n), net) && 695be01d655SYOSHIFUJI Hideaki (n->dev == dev || !n->dev)) 696fa86d322SPavel Emelyanov return n; 697be01d655SYOSHIFUJI Hideaki n = n->next; 698be01d655SYOSHIFUJI Hideaki } 699be01d655SYOSHIFUJI Hideaki return NULL; 700be01d655SYOSHIFUJI Hideaki } 701be01d655SYOSHIFUJI Hideaki 702be01d655SYOSHIFUJI Hideaki struct pneigh_entry *__pneigh_lookup(struct neigh_table *tbl, 703be01d655SYOSHIFUJI Hideaki struct net *net, const void *pkey, struct net_device *dev) 704be01d655SYOSHIFUJI Hideaki { 70501ccdf12SAlexey Dobriyan unsigned int key_len = tbl->key_len; 706be01d655SYOSHIFUJI Hideaki u32 hash_val = pneigh_hash(pkey, key_len); 707be01d655SYOSHIFUJI Hideaki 708be01d655SYOSHIFUJI Hideaki return __pneigh_lookup_1(tbl->phash_buckets[hash_val], 709be01d655SYOSHIFUJI Hideaki net, pkey, key_len, dev); 710fa86d322SPavel Emelyanov } 7110a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL_GPL(__pneigh_lookup); 712fa86d322SPavel Emelyanov 713426b5303SEric W. Biederman struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, 714426b5303SEric W. Biederman struct net *net, const void *pkey, 7151da177e4SLinus Torvalds struct net_device *dev, int creat) 7161da177e4SLinus Torvalds { 7171da177e4SLinus Torvalds struct pneigh_entry *n; 71801ccdf12SAlexey Dobriyan unsigned int key_len = tbl->key_len; 719be01d655SYOSHIFUJI Hideaki u32 hash_val = pneigh_hash(pkey, key_len); 7201da177e4SLinus Torvalds 7211da177e4SLinus Torvalds read_lock_bh(&tbl->lock); 722be01d655SYOSHIFUJI Hideaki n = __pneigh_lookup_1(tbl->phash_buckets[hash_val], 723be01d655SYOSHIFUJI Hideaki net, pkey, key_len, dev); 724be01d655SYOSHIFUJI Hideaki read_unlock_bh(&tbl->lock); 7251da177e4SLinus Torvalds 726be01d655SYOSHIFUJI Hideaki if (n || !creat) 7271da177e4SLinus Torvalds goto out; 7281da177e4SLinus Torvalds 7294ae28944SPavel Emelyanov ASSERT_RTNL(); 7304ae28944SPavel Emelyanov 7311da177e4SLinus Torvalds n = kmalloc(sizeof(*n) + key_len, GFP_KERNEL); 7321da177e4SLinus Torvalds if (!n) 7331da177e4SLinus Torvalds goto out; 7341da177e4SLinus Torvalds 735754d5da6SDavid Ahern n->protocol = 0; 736efd7ef1cSEric W. Biederman write_pnet(&n->net, net); 7371da177e4SLinus Torvalds memcpy(n->key, pkey, key_len); 7381da177e4SLinus Torvalds n->dev = dev; 7391da177e4SLinus Torvalds if (dev) 7401da177e4SLinus Torvalds dev_hold(dev); 7411da177e4SLinus Torvalds 7421da177e4SLinus Torvalds if (tbl->pconstructor && tbl->pconstructor(n)) { 7431da177e4SLinus Torvalds if (dev) 7441da177e4SLinus Torvalds dev_put(dev); 7451da177e4SLinus Torvalds kfree(n); 7461da177e4SLinus Torvalds n = NULL; 7471da177e4SLinus Torvalds goto out; 7481da177e4SLinus Torvalds } 7491da177e4SLinus Torvalds 7501da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 7511da177e4SLinus Torvalds n->next = tbl->phash_buckets[hash_val]; 7521da177e4SLinus Torvalds tbl->phash_buckets[hash_val] = n; 7531da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 7541da177e4SLinus Torvalds out: 7551da177e4SLinus Torvalds return n; 7561da177e4SLinus Torvalds } 7570a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(pneigh_lookup); 7581da177e4SLinus Torvalds 7591da177e4SLinus Torvalds 760426b5303SEric W. Biederman int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey, 7611da177e4SLinus Torvalds struct net_device *dev) 7621da177e4SLinus Torvalds { 7631da177e4SLinus Torvalds struct pneigh_entry *n, **np; 76401ccdf12SAlexey Dobriyan unsigned int key_len = tbl->key_len; 765be01d655SYOSHIFUJI Hideaki u32 hash_val = pneigh_hash(pkey, key_len); 7661da177e4SLinus Torvalds 7671da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 7681da177e4SLinus Torvalds for (np = &tbl->phash_buckets[hash_val]; (n = *np) != NULL; 7691da177e4SLinus Torvalds np = &n->next) { 770426b5303SEric W. Biederman if (!memcmp(n->key, pkey, key_len) && n->dev == dev && 771878628fbSYOSHIFUJI Hideaki net_eq(pneigh_net(n), net)) { 7721da177e4SLinus Torvalds *np = n->next; 7731da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 7741da177e4SLinus Torvalds if (tbl->pdestructor) 7751da177e4SLinus Torvalds tbl->pdestructor(n); 7761da177e4SLinus Torvalds if (n->dev) 7771da177e4SLinus Torvalds dev_put(n->dev); 7781da177e4SLinus Torvalds kfree(n); 7791da177e4SLinus Torvalds return 0; 7801da177e4SLinus Torvalds } 7811da177e4SLinus Torvalds } 7821da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 7831da177e4SLinus Torvalds return -ENOENT; 7841da177e4SLinus Torvalds } 7851da177e4SLinus Torvalds 78653b76cdfSWolfgang Bumiller static int pneigh_ifdown_and_unlock(struct neigh_table *tbl, 78753b76cdfSWolfgang Bumiller struct net_device *dev) 7881da177e4SLinus Torvalds { 78953b76cdfSWolfgang Bumiller struct pneigh_entry *n, **np, *freelist = NULL; 7901da177e4SLinus Torvalds u32 h; 7911da177e4SLinus Torvalds 7921da177e4SLinus Torvalds for (h = 0; h <= PNEIGH_HASHMASK; h++) { 7931da177e4SLinus Torvalds np = &tbl->phash_buckets[h]; 7941da177e4SLinus Torvalds while ((n = *np) != NULL) { 7951da177e4SLinus Torvalds if (!dev || n->dev == dev) { 7961da177e4SLinus Torvalds *np = n->next; 79753b76cdfSWolfgang Bumiller n->next = freelist; 79853b76cdfSWolfgang Bumiller freelist = n; 79953b76cdfSWolfgang Bumiller continue; 80053b76cdfSWolfgang Bumiller } 80153b76cdfSWolfgang Bumiller np = &n->next; 80253b76cdfSWolfgang Bumiller } 80353b76cdfSWolfgang Bumiller } 80453b76cdfSWolfgang Bumiller write_unlock_bh(&tbl->lock); 80553b76cdfSWolfgang Bumiller while ((n = freelist)) { 80653b76cdfSWolfgang Bumiller freelist = n->next; 80753b76cdfSWolfgang Bumiller n->next = NULL; 8081da177e4SLinus Torvalds if (tbl->pdestructor) 8091da177e4SLinus Torvalds tbl->pdestructor(n); 8101da177e4SLinus Torvalds if (n->dev) 8111da177e4SLinus Torvalds dev_put(n->dev); 8121da177e4SLinus Torvalds kfree(n); 8131da177e4SLinus Torvalds } 8141da177e4SLinus Torvalds return -ENOENT; 8151da177e4SLinus Torvalds } 8161da177e4SLinus Torvalds 81706f0511dSDenis V. Lunev static void neigh_parms_destroy(struct neigh_parms *parms); 81806f0511dSDenis V. Lunev 81906f0511dSDenis V. Lunev static inline void neigh_parms_put(struct neigh_parms *parms) 82006f0511dSDenis V. Lunev { 8216343944bSReshetova, Elena if (refcount_dec_and_test(&parms->refcnt)) 82206f0511dSDenis V. Lunev neigh_parms_destroy(parms); 82306f0511dSDenis V. Lunev } 8241da177e4SLinus Torvalds 8251da177e4SLinus Torvalds /* 8261da177e4SLinus Torvalds * neighbour must already be out of the table; 8271da177e4SLinus Torvalds * 8281da177e4SLinus Torvalds */ 8291da177e4SLinus Torvalds void neigh_destroy(struct neighbour *neigh) 8301da177e4SLinus Torvalds { 831da6a8fa0SDavid Miller struct net_device *dev = neigh->dev; 832da6a8fa0SDavid Miller 8331da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(neigh->tbl, destroys); 8341da177e4SLinus Torvalds 8351da177e4SLinus Torvalds if (!neigh->dead) { 836e005d193SJoe Perches pr_warn("Destroying alive neighbour %p\n", neigh); 8371da177e4SLinus Torvalds dump_stack(); 8381da177e4SLinus Torvalds return; 8391da177e4SLinus Torvalds } 8401da177e4SLinus Torvalds 8411da177e4SLinus Torvalds if (neigh_del_timer(neigh)) 842e005d193SJoe Perches pr_warn("Impossible event\n"); 8431da177e4SLinus Torvalds 844c9ab4d85SEric Dumazet write_lock_bh(&neigh->lock); 845c9ab4d85SEric Dumazet __skb_queue_purge(&neigh->arp_queue); 846c9ab4d85SEric Dumazet write_unlock_bh(&neigh->lock); 8478b5c171bSEric Dumazet neigh->arp_queue_len_bytes = 0; 8481da177e4SLinus Torvalds 849447f2191SDavid S. Miller if (dev->netdev_ops->ndo_neigh_destroy) 850503eebc2SJiri Pirko dev->netdev_ops->ndo_neigh_destroy(dev, neigh); 851447f2191SDavid S. Miller 852da6a8fa0SDavid Miller dev_put(dev); 8531da177e4SLinus Torvalds neigh_parms_put(neigh->parms); 8541da177e4SLinus Torvalds 855d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is destroyed\n", neigh); 8561da177e4SLinus Torvalds 8571da177e4SLinus Torvalds atomic_dec(&neigh->tbl->entries); 8585b8b0060SDavid Miller kfree_rcu(neigh, rcu); 8591da177e4SLinus Torvalds } 8600a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_destroy); 8611da177e4SLinus Torvalds 8621da177e4SLinus Torvalds /* Neighbour state is suspicious; 8631da177e4SLinus Torvalds disable fast path. 8641da177e4SLinus Torvalds 8651da177e4SLinus Torvalds Called with write_locked neigh. 8661da177e4SLinus Torvalds */ 8671da177e4SLinus Torvalds static void neigh_suspect(struct neighbour *neigh) 8681da177e4SLinus Torvalds { 869d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is suspected\n", neigh); 8701da177e4SLinus Torvalds 8711da177e4SLinus Torvalds neigh->output = neigh->ops->output; 8721da177e4SLinus Torvalds } 8731da177e4SLinus Torvalds 8741da177e4SLinus Torvalds /* Neighbour state is OK; 8751da177e4SLinus Torvalds enable fast path. 8761da177e4SLinus Torvalds 8771da177e4SLinus Torvalds Called with write_locked neigh. 8781da177e4SLinus Torvalds */ 8791da177e4SLinus Torvalds static void neigh_connect(struct neighbour *neigh) 8801da177e4SLinus Torvalds { 881d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is connected\n", neigh); 8821da177e4SLinus Torvalds 8831da177e4SLinus Torvalds neigh->output = neigh->ops->connected_output; 8841da177e4SLinus Torvalds } 8851da177e4SLinus Torvalds 886e4c4e448SEric Dumazet static void neigh_periodic_work(struct work_struct *work) 8871da177e4SLinus Torvalds { 888e4c4e448SEric Dumazet struct neigh_table *tbl = container_of(work, struct neigh_table, gc_work.work); 889767e97e1SEric Dumazet struct neighbour *n; 890767e97e1SEric Dumazet struct neighbour __rcu **np; 891e4c4e448SEric Dumazet unsigned int i; 892d6bf7817SEric Dumazet struct neigh_hash_table *nht; 8931da177e4SLinus Torvalds 8941da177e4SLinus Torvalds NEIGH_CACHE_STAT_INC(tbl, periodic_gc_runs); 8951da177e4SLinus Torvalds 896e4c4e448SEric Dumazet write_lock_bh(&tbl->lock); 897d6bf7817SEric Dumazet nht = rcu_dereference_protected(tbl->nht, 898d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 8991da177e4SLinus Torvalds 9001da177e4SLinus Torvalds /* 9011da177e4SLinus Torvalds * periodically recompute ReachableTime from random function 9021da177e4SLinus Torvalds */ 9031da177e4SLinus Torvalds 904e4c4e448SEric Dumazet if (time_after(jiffies, tbl->last_rand + 300 * HZ)) { 9051da177e4SLinus Torvalds struct neigh_parms *p; 906e4c4e448SEric Dumazet tbl->last_rand = jiffies; 90775fbfd33SNicolas Dichtel list_for_each_entry(p, &tbl->parms_list, list) 9081da177e4SLinus Torvalds p->reachable_time = 9091f9248e5SJiri Pirko neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); 9101da177e4SLinus Torvalds } 9111da177e4SLinus Torvalds 912feff9ab2SDuan Jiong if (atomic_read(&tbl->entries) < tbl->gc_thresh1) 913feff9ab2SDuan Jiong goto out; 914feff9ab2SDuan Jiong 915cd089336SDavid S. Miller for (i = 0 ; i < (1 << nht->hash_shift); i++) { 916d6bf7817SEric Dumazet np = &nht->hash_buckets[i]; 9171da177e4SLinus Torvalds 918767e97e1SEric Dumazet while ((n = rcu_dereference_protected(*np, 919767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))) != NULL) { 9201da177e4SLinus Torvalds unsigned int state; 9211da177e4SLinus Torvalds 9221da177e4SLinus Torvalds write_lock(&n->lock); 9231da177e4SLinus Torvalds 9241da177e4SLinus Torvalds state = n->nud_state; 9259ce33e46SRoopa Prabhu if ((state & (NUD_PERMANENT | NUD_IN_TIMER)) || 9269ce33e46SRoopa Prabhu (n->flags & NTF_EXT_LEARNED)) { 9271da177e4SLinus Torvalds write_unlock(&n->lock); 9281da177e4SLinus Torvalds goto next_elt; 9291da177e4SLinus Torvalds } 9301da177e4SLinus Torvalds 9311da177e4SLinus Torvalds if (time_before(n->used, n->confirmed)) 9321da177e4SLinus Torvalds n->used = n->confirmed; 9331da177e4SLinus Torvalds 9349f237430SReshetova, Elena if (refcount_read(&n->refcnt) == 1 && 9351da177e4SLinus Torvalds (state == NUD_FAILED || 9361f9248e5SJiri Pirko time_after(jiffies, n->used + NEIGH_VAR(n->parms, GC_STALETIME)))) { 9371da177e4SLinus Torvalds *np = n->next; 93858956317SDavid Ahern neigh_mark_dead(n); 9391da177e4SLinus Torvalds write_unlock(&n->lock); 9404f494554SThomas Graf neigh_cleanup_and_release(n); 9411da177e4SLinus Torvalds continue; 9421da177e4SLinus Torvalds } 9431da177e4SLinus Torvalds write_unlock(&n->lock); 9441da177e4SLinus Torvalds 9451da177e4SLinus Torvalds next_elt: 9461da177e4SLinus Torvalds np = &n->next; 9471da177e4SLinus Torvalds } 948e4c4e448SEric Dumazet /* 949e4c4e448SEric Dumazet * It's fine to release lock here, even if hash table 950e4c4e448SEric Dumazet * grows while we are preempted. 951e4c4e448SEric Dumazet */ 952e4c4e448SEric Dumazet write_unlock_bh(&tbl->lock); 953e4c4e448SEric Dumazet cond_resched(); 954e4c4e448SEric Dumazet write_lock_bh(&tbl->lock); 95584338a6cSMichel Machado nht = rcu_dereference_protected(tbl->nht, 95684338a6cSMichel Machado lockdep_is_held(&tbl->lock)); 957e4c4e448SEric Dumazet } 9582724680bSYOSHIFUJI Hideaki / 吉藤英明 out: 9591f9248e5SJiri Pirko /* Cycle through all hash buckets every BASE_REACHABLE_TIME/2 ticks. 9601f9248e5SJiri Pirko * ARP entry timeouts range from 1/2 BASE_REACHABLE_TIME to 3/2 9611f9248e5SJiri Pirko * BASE_REACHABLE_TIME. 9621da177e4SLinus Torvalds */ 963f618002bSviresh kumar queue_delayed_work(system_power_efficient_wq, &tbl->gc_work, 9641f9248e5SJiri Pirko NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME) >> 1); 965e4c4e448SEric Dumazet write_unlock_bh(&tbl->lock); 9661da177e4SLinus Torvalds } 9671da177e4SLinus Torvalds 9681da177e4SLinus Torvalds static __inline__ int neigh_max_probes(struct neighbour *n) 9691da177e4SLinus Torvalds { 9701da177e4SLinus Torvalds struct neigh_parms *p = n->parms; 9718da86466SYOSHIFUJI Hideaki/吉藤英明 return NEIGH_VAR(p, UCAST_PROBES) + NEIGH_VAR(p, APP_PROBES) + 9728da86466SYOSHIFUJI Hideaki/吉藤英明 (n->nud_state & NUD_PROBE ? NEIGH_VAR(p, MCAST_REPROBES) : 9738da86466SYOSHIFUJI Hideaki/吉藤英明 NEIGH_VAR(p, MCAST_PROBES)); 9741da177e4SLinus Torvalds } 9751da177e4SLinus Torvalds 9765ef12d98STimo Teras static void neigh_invalidate(struct neighbour *neigh) 9770a141509SEric Dumazet __releases(neigh->lock) 9780a141509SEric Dumazet __acquires(neigh->lock) 9795ef12d98STimo Teras { 9805ef12d98STimo Teras struct sk_buff *skb; 9815ef12d98STimo Teras 9825ef12d98STimo Teras NEIGH_CACHE_STAT_INC(neigh->tbl, res_failed); 983d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is failed\n", neigh); 9845ef12d98STimo Teras neigh->updated = jiffies; 9855ef12d98STimo Teras 9865ef12d98STimo Teras /* It is very thin place. report_unreachable is very complicated 9875ef12d98STimo Teras routine. Particularly, it can hit the same neighbour entry! 9885ef12d98STimo Teras 9895ef12d98STimo Teras So that, we try to be accurate and avoid dead loop. --ANK 9905ef12d98STimo Teras */ 9915ef12d98STimo Teras while (neigh->nud_state == NUD_FAILED && 9925ef12d98STimo Teras (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) { 9935ef12d98STimo Teras write_unlock(&neigh->lock); 9945ef12d98STimo Teras neigh->ops->error_report(neigh, skb); 9955ef12d98STimo Teras write_lock(&neigh->lock); 9965ef12d98STimo Teras } 997c9ab4d85SEric Dumazet __skb_queue_purge(&neigh->arp_queue); 9988b5c171bSEric Dumazet neigh->arp_queue_len_bytes = 0; 9995ef12d98STimo Teras } 10005ef12d98STimo Teras 1001cd28ca0aSEric Dumazet static void neigh_probe(struct neighbour *neigh) 1002cd28ca0aSEric Dumazet __releases(neigh->lock) 1003cd28ca0aSEric Dumazet { 10044ed377e3SHannes Frederic Sowa struct sk_buff *skb = skb_peek_tail(&neigh->arp_queue); 1005cd28ca0aSEric Dumazet /* keep skb alive even if arp_queue overflows */ 1006cd28ca0aSEric Dumazet if (skb) 100719125c1aSMartin Zhang skb = skb_clone(skb, GFP_ATOMIC); 1008cd28ca0aSEric Dumazet write_unlock(&neigh->lock); 100948481c8fSEric Dumazet if (neigh->ops->solicit) 1010cd28ca0aSEric Dumazet neigh->ops->solicit(neigh, skb); 1011cd28ca0aSEric Dumazet atomic_inc(&neigh->probes); 101287fff3caSYang Wei consume_skb(skb); 1013cd28ca0aSEric Dumazet } 1014cd28ca0aSEric Dumazet 10151da177e4SLinus Torvalds /* Called when a timer expires for a neighbour entry. */ 10161da177e4SLinus Torvalds 1017e99e88a9SKees Cook static void neigh_timer_handler(struct timer_list *t) 10181da177e4SLinus Torvalds { 10191da177e4SLinus Torvalds unsigned long now, next; 1020e99e88a9SKees Cook struct neighbour *neigh = from_timer(neigh, t, timer); 102195c96174SEric Dumazet unsigned int state; 10221da177e4SLinus Torvalds int notify = 0; 10231da177e4SLinus Torvalds 10241da177e4SLinus Torvalds write_lock(&neigh->lock); 10251da177e4SLinus Torvalds 10261da177e4SLinus Torvalds state = neigh->nud_state; 10271da177e4SLinus Torvalds now = jiffies; 10281da177e4SLinus Torvalds next = now + HZ; 10291da177e4SLinus Torvalds 1030045f7b3bSDavid S. Miller if (!(state & NUD_IN_TIMER)) 10311da177e4SLinus Torvalds goto out; 10321da177e4SLinus Torvalds 10331da177e4SLinus Torvalds if (state & NUD_REACHABLE) { 10341da177e4SLinus Torvalds if (time_before_eq(now, 10351da177e4SLinus Torvalds neigh->confirmed + neigh->parms->reachable_time)) { 1036d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is still alive\n", neigh); 10371da177e4SLinus Torvalds next = neigh->confirmed + neigh->parms->reachable_time; 10381da177e4SLinus Torvalds } else if (time_before_eq(now, 10391f9248e5SJiri Pirko neigh->used + 10401f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) { 1041d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is delayed\n", neigh); 10421da177e4SLinus Torvalds neigh->nud_state = NUD_DELAY; 1043955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 10441da177e4SLinus Torvalds neigh_suspect(neigh); 10451f9248e5SJiri Pirko next = now + NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME); 10461da177e4SLinus Torvalds } else { 1047d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is suspected\n", neigh); 10481da177e4SLinus Torvalds neigh->nud_state = NUD_STALE; 1049955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 10501da177e4SLinus Torvalds neigh_suspect(neigh); 10518d71740cSTom Tucker notify = 1; 10521da177e4SLinus Torvalds } 10531da177e4SLinus Torvalds } else if (state & NUD_DELAY) { 10541da177e4SLinus Torvalds if (time_before_eq(now, 10551f9248e5SJiri Pirko neigh->confirmed + 10561f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) { 1057d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is now reachable\n", neigh); 10581da177e4SLinus Torvalds neigh->nud_state = NUD_REACHABLE; 1059955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 10601da177e4SLinus Torvalds neigh_connect(neigh); 10618d71740cSTom Tucker notify = 1; 10621da177e4SLinus Torvalds next = neigh->confirmed + neigh->parms->reachable_time; 10631da177e4SLinus Torvalds } else { 1064d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is probed\n", neigh); 10651da177e4SLinus Torvalds neigh->nud_state = NUD_PROBE; 1066955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 10671da177e4SLinus Torvalds atomic_set(&neigh->probes, 0); 1068765c9c63SErik Kline notify = 1; 10691f9248e5SJiri Pirko next = now + NEIGH_VAR(neigh->parms, RETRANS_TIME); 10701da177e4SLinus Torvalds } 10711da177e4SLinus Torvalds } else { 10721da177e4SLinus Torvalds /* NUD_PROBE|NUD_INCOMPLETE */ 10731f9248e5SJiri Pirko next = now + NEIGH_VAR(neigh->parms, RETRANS_TIME); 10741da177e4SLinus Torvalds } 10751da177e4SLinus Torvalds 10761da177e4SLinus Torvalds if ((neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) && 10771da177e4SLinus Torvalds atomic_read(&neigh->probes) >= neigh_max_probes(neigh)) { 10781da177e4SLinus Torvalds neigh->nud_state = NUD_FAILED; 10791da177e4SLinus Torvalds notify = 1; 10805ef12d98STimo Teras neigh_invalidate(neigh); 10815e2c21dcSDuan Jiong goto out; 10821da177e4SLinus Torvalds } 10831da177e4SLinus Torvalds 10841da177e4SLinus Torvalds if (neigh->nud_state & NUD_IN_TIMER) { 10851da177e4SLinus Torvalds if (time_before(next, jiffies + HZ/2)) 10861da177e4SLinus Torvalds next = jiffies + HZ/2; 10876fb9974fSHerbert Xu if (!mod_timer(&neigh->timer, next)) 10886fb9974fSHerbert Xu neigh_hold(neigh); 10891da177e4SLinus Torvalds } 10901da177e4SLinus Torvalds if (neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) { 1091cd28ca0aSEric Dumazet neigh_probe(neigh); 10929ff56607SDavid S. Miller } else { 10931da177e4SLinus Torvalds out: 10941da177e4SLinus Torvalds write_unlock(&neigh->lock); 10959ff56607SDavid S. Miller } 10961da177e4SLinus Torvalds 1097d961db35SThomas Graf if (notify) 10987b8f7a40SRoopa Prabhu neigh_update_notify(neigh, 0); 1099d961db35SThomas Graf 110056dd18a4SRoopa Prabhu trace_neigh_timer_handler(neigh, 0); 110156dd18a4SRoopa Prabhu 11021da177e4SLinus Torvalds neigh_release(neigh); 11031da177e4SLinus Torvalds } 11041da177e4SLinus Torvalds 11051da177e4SLinus Torvalds int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) 11061da177e4SLinus Torvalds { 11071da177e4SLinus Torvalds int rc; 1108cd28ca0aSEric Dumazet bool immediate_probe = false; 11091da177e4SLinus Torvalds 11101da177e4SLinus Torvalds write_lock_bh(&neigh->lock); 11111da177e4SLinus Torvalds 11121da177e4SLinus Torvalds rc = 0; 11131da177e4SLinus Torvalds if (neigh->nud_state & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE)) 11141da177e4SLinus Torvalds goto out_unlock_bh; 11152c51a97fSJulian Anastasov if (neigh->dead) 11162c51a97fSJulian Anastasov goto out_dead; 11171da177e4SLinus Torvalds 11181da177e4SLinus Torvalds if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) { 11191f9248e5SJiri Pirko if (NEIGH_VAR(neigh->parms, MCAST_PROBES) + 11201f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, APP_PROBES)) { 1121cd28ca0aSEric Dumazet unsigned long next, now = jiffies; 1122cd28ca0aSEric Dumazet 11231f9248e5SJiri Pirko atomic_set(&neigh->probes, 11241f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, UCAST_PROBES)); 11251da177e4SLinus Torvalds neigh->nud_state = NUD_INCOMPLETE; 1126cd28ca0aSEric Dumazet neigh->updated = now; 11271f9248e5SJiri Pirko next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), 11281f9248e5SJiri Pirko HZ/2); 1129cd28ca0aSEric Dumazet neigh_add_timer(neigh, next); 1130cd28ca0aSEric Dumazet immediate_probe = true; 11311da177e4SLinus Torvalds } else { 11321da177e4SLinus Torvalds neigh->nud_state = NUD_FAILED; 1133955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 11341da177e4SLinus Torvalds write_unlock_bh(&neigh->lock); 11351da177e4SLinus Torvalds 11361da177e4SLinus Torvalds kfree_skb(skb); 11371da177e4SLinus Torvalds return 1; 11381da177e4SLinus Torvalds } 11391da177e4SLinus Torvalds } else if (neigh->nud_state & NUD_STALE) { 1140d5d427cdSJoe Perches neigh_dbg(2, "neigh %p is delayed\n", neigh); 11411da177e4SLinus Torvalds neigh->nud_state = NUD_DELAY; 1142955aaa2fSYOSHIFUJI Hideaki neigh->updated = jiffies; 11431f9248e5SJiri Pirko neigh_add_timer(neigh, jiffies + 11441f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME)); 11451da177e4SLinus Torvalds } 11461da177e4SLinus Torvalds 11471da177e4SLinus Torvalds if (neigh->nud_state == NUD_INCOMPLETE) { 11481da177e4SLinus Torvalds if (skb) { 11498b5c171bSEric Dumazet while (neigh->arp_queue_len_bytes + skb->truesize > 11501f9248e5SJiri Pirko NEIGH_VAR(neigh->parms, QUEUE_LEN_BYTES)) { 11511da177e4SLinus Torvalds struct sk_buff *buff; 11528b5c171bSEric Dumazet 1153f72051b0SDavid S. Miller buff = __skb_dequeue(&neigh->arp_queue); 11548b5c171bSEric Dumazet if (!buff) 11558b5c171bSEric Dumazet break; 11568b5c171bSEric Dumazet neigh->arp_queue_len_bytes -= buff->truesize; 11571da177e4SLinus Torvalds kfree_skb(buff); 11589a6d276eSNeil Horman NEIGH_CACHE_STAT_INC(neigh->tbl, unres_discards); 11591da177e4SLinus Torvalds } 1160a4731138SEric Dumazet skb_dst_force(skb); 11611da177e4SLinus Torvalds __skb_queue_tail(&neigh->arp_queue, skb); 11628b5c171bSEric Dumazet neigh->arp_queue_len_bytes += skb->truesize; 11631da177e4SLinus Torvalds } 11641da177e4SLinus Torvalds rc = 1; 11651da177e4SLinus Torvalds } 11661da177e4SLinus Torvalds out_unlock_bh: 1167cd28ca0aSEric Dumazet if (immediate_probe) 1168cd28ca0aSEric Dumazet neigh_probe(neigh); 1169cd28ca0aSEric Dumazet else 1170cd28ca0aSEric Dumazet write_unlock(&neigh->lock); 1171cd28ca0aSEric Dumazet local_bh_enable(); 117256dd18a4SRoopa Prabhu trace_neigh_event_send_done(neigh, rc); 11731da177e4SLinus Torvalds return rc; 11742c51a97fSJulian Anastasov 11752c51a97fSJulian Anastasov out_dead: 11762c51a97fSJulian Anastasov if (neigh->nud_state & NUD_STALE) 11772c51a97fSJulian Anastasov goto out_unlock_bh; 11782c51a97fSJulian Anastasov write_unlock_bh(&neigh->lock); 11792c51a97fSJulian Anastasov kfree_skb(skb); 118056dd18a4SRoopa Prabhu trace_neigh_event_send_dead(neigh, 1); 11812c51a97fSJulian Anastasov return 1; 11821da177e4SLinus Torvalds } 11830a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(__neigh_event_send); 11841da177e4SLinus Torvalds 1185f6b72b62SDavid S. Miller static void neigh_update_hhs(struct neighbour *neigh) 11861da177e4SLinus Torvalds { 11871da177e4SLinus Torvalds struct hh_cache *hh; 11883b04dddeSStephen Hemminger void (*update)(struct hh_cache*, const struct net_device*, const unsigned char *) 118991a72a70SDoug Kehn = NULL; 119091a72a70SDoug Kehn 119191a72a70SDoug Kehn if (neigh->dev->header_ops) 119291a72a70SDoug Kehn update = neigh->dev->header_ops->cache_update; 11931da177e4SLinus Torvalds 11941da177e4SLinus Torvalds if (update) { 1195f6b72b62SDavid S. Miller hh = &neigh->hh; 1196f6b72b62SDavid S. Miller if (hh->hh_len) { 11973644f0ceSStephen Hemminger write_seqlock_bh(&hh->hh_lock); 11981da177e4SLinus Torvalds update(hh, neigh->dev, neigh->ha); 11993644f0ceSStephen Hemminger write_sequnlock_bh(&hh->hh_lock); 12001da177e4SLinus Torvalds } 12011da177e4SLinus Torvalds } 12021da177e4SLinus Torvalds } 12031da177e4SLinus Torvalds 12041da177e4SLinus Torvalds 12051da177e4SLinus Torvalds 12061da177e4SLinus Torvalds /* Generic update routine. 12071da177e4SLinus Torvalds -- lladdr is new lladdr or NULL, if it is not supplied. 12081da177e4SLinus Torvalds -- new is new state. 12091da177e4SLinus Torvalds -- flags 12101da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE allows to override existing lladdr, 12111da177e4SLinus Torvalds if it is different. 12121da177e4SLinus Torvalds NEIGH_UPDATE_F_WEAK_OVERRIDE will suspect existing "connected" 12131da177e4SLinus Torvalds lladdr instead of overriding it 12141da177e4SLinus Torvalds if it is different. 12151da177e4SLinus Torvalds NEIGH_UPDATE_F_ADMIN means that the change is administrative. 12161da177e4SLinus Torvalds 12171da177e4SLinus Torvalds NEIGH_UPDATE_F_OVERRIDE_ISROUTER allows to override existing 12181da177e4SLinus Torvalds NTF_ROUTER flag. 12191da177e4SLinus Torvalds NEIGH_UPDATE_F_ISROUTER indicates if the neighbour is known as 12201da177e4SLinus Torvalds a router. 12211da177e4SLinus Torvalds 12221da177e4SLinus Torvalds Caller MUST hold reference count on the entry. 12231da177e4SLinus Torvalds */ 12241da177e4SLinus Torvalds 12257a35a50dSDavid Ahern static int __neigh_update(struct neighbour *neigh, const u8 *lladdr, 12267a35a50dSDavid Ahern u8 new, u32 flags, u32 nlmsg_pid, 12277a35a50dSDavid Ahern struct netlink_ext_ack *extack) 12281da177e4SLinus Torvalds { 1229e997f8a2SDavid Ahern bool ext_learn_change = false; 12301da177e4SLinus Torvalds u8 old; 12311da177e4SLinus Torvalds int err; 12321da177e4SLinus Torvalds int notify = 0; 12331da177e4SLinus Torvalds struct net_device *dev; 12341da177e4SLinus Torvalds int update_isrouter = 0; 12351da177e4SLinus Torvalds 123656dd18a4SRoopa Prabhu trace_neigh_update(neigh, lladdr, new, flags, nlmsg_pid); 123756dd18a4SRoopa Prabhu 12381da177e4SLinus Torvalds write_lock_bh(&neigh->lock); 12391da177e4SLinus Torvalds 12401da177e4SLinus Torvalds dev = neigh->dev; 12411da177e4SLinus Torvalds old = neigh->nud_state; 12421da177e4SLinus Torvalds err = -EPERM; 12431da177e4SLinus Torvalds 12441da177e4SLinus Torvalds if (!(flags & NEIGH_UPDATE_F_ADMIN) && 12451da177e4SLinus Torvalds (old & (NUD_NOARP | NUD_PERMANENT))) 12461da177e4SLinus Torvalds goto out; 12477a35a50dSDavid Ahern if (neigh->dead) { 12487a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Neighbor entry is now dead"); 12492c51a97fSJulian Anastasov goto out; 12507a35a50dSDavid Ahern } 12511da177e4SLinus Torvalds 1252e997f8a2SDavid Ahern ext_learn_change = neigh_update_ext_learned(neigh, flags, ¬ify); 12539ce33e46SRoopa Prabhu 12541da177e4SLinus Torvalds if (!(new & NUD_VALID)) { 12551da177e4SLinus Torvalds neigh_del_timer(neigh); 12561da177e4SLinus Torvalds if (old & NUD_CONNECTED) 12571da177e4SLinus Torvalds neigh_suspect(neigh); 12589c29a2f5SDavid Ahern neigh->nud_state = new; 12591da177e4SLinus Torvalds err = 0; 12601da177e4SLinus Torvalds notify = old & NUD_VALID; 1261d2fb4fb8SRoopa Prabhu if ((old & (NUD_INCOMPLETE | NUD_PROBE)) && 12625ef12d98STimo Teras (new & NUD_FAILED)) { 12635ef12d98STimo Teras neigh_invalidate(neigh); 12645ef12d98STimo Teras notify = 1; 12655ef12d98STimo Teras } 12661da177e4SLinus Torvalds goto out; 12671da177e4SLinus Torvalds } 12681da177e4SLinus Torvalds 12691da177e4SLinus Torvalds /* Compare new lladdr with cached one */ 12701da177e4SLinus Torvalds if (!dev->addr_len) { 12711da177e4SLinus Torvalds /* First case: device needs no address. */ 12721da177e4SLinus Torvalds lladdr = neigh->ha; 12731da177e4SLinus Torvalds } else if (lladdr) { 12741da177e4SLinus Torvalds /* The second case: if something is already cached 12751da177e4SLinus Torvalds and a new address is proposed: 12761da177e4SLinus Torvalds - compare new & old 12771da177e4SLinus Torvalds - if they are different, check override flag 12781da177e4SLinus Torvalds */ 12791da177e4SLinus Torvalds if ((old & NUD_VALID) && 12801da177e4SLinus Torvalds !memcmp(lladdr, neigh->ha, dev->addr_len)) 12811da177e4SLinus Torvalds lladdr = neigh->ha; 12821da177e4SLinus Torvalds } else { 12831da177e4SLinus Torvalds /* No address is supplied; if we know something, 12841da177e4SLinus Torvalds use it, otherwise discard the request. 12851da177e4SLinus Torvalds */ 12861da177e4SLinus Torvalds err = -EINVAL; 12877a35a50dSDavid Ahern if (!(old & NUD_VALID)) { 12887a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "No link layer address given"); 12891da177e4SLinus Torvalds goto out; 12907a35a50dSDavid Ahern } 12911da177e4SLinus Torvalds lladdr = neigh->ha; 12921da177e4SLinus Torvalds } 12931da177e4SLinus Torvalds 1294f0e0d044SVasily Khoruzhick /* Update confirmed timestamp for neighbour entry after we 1295f0e0d044SVasily Khoruzhick * received ARP packet even if it doesn't change IP to MAC binding. 1296f0e0d044SVasily Khoruzhick */ 1297f0e0d044SVasily Khoruzhick if (new & NUD_CONNECTED) 1298f0e0d044SVasily Khoruzhick neigh->confirmed = jiffies; 1299f0e0d044SVasily Khoruzhick 13001da177e4SLinus Torvalds /* If entry was valid and address is not changed, 13011da177e4SLinus Torvalds do not change entry state, if new one is STALE. 13021da177e4SLinus Torvalds */ 13031da177e4SLinus Torvalds err = 0; 13041da177e4SLinus Torvalds update_isrouter = flags & NEIGH_UPDATE_F_OVERRIDE_ISROUTER; 13051da177e4SLinus Torvalds if (old & NUD_VALID) { 13061da177e4SLinus Torvalds if (lladdr != neigh->ha && !(flags & NEIGH_UPDATE_F_OVERRIDE)) { 13071da177e4SLinus Torvalds update_isrouter = 0; 13081da177e4SLinus Torvalds if ((flags & NEIGH_UPDATE_F_WEAK_OVERRIDE) && 13091da177e4SLinus Torvalds (old & NUD_CONNECTED)) { 13101da177e4SLinus Torvalds lladdr = neigh->ha; 13111da177e4SLinus Torvalds new = NUD_STALE; 13121da177e4SLinus Torvalds } else 13131da177e4SLinus Torvalds goto out; 13141da177e4SLinus Torvalds } else { 13150e7bbcc1SJulian Anastasov if (lladdr == neigh->ha && new == NUD_STALE && 13160e7bbcc1SJulian Anastasov !(flags & NEIGH_UPDATE_F_ADMIN)) 13171da177e4SLinus Torvalds new = old; 13181da177e4SLinus Torvalds } 13191da177e4SLinus Torvalds } 13201da177e4SLinus Torvalds 1321f0e0d044SVasily Khoruzhick /* Update timestamp only once we know we will make a change to the 132277d71233SIhar Hrachyshka * neighbour entry. Otherwise we risk to move the locktime window with 132377d71233SIhar Hrachyshka * noop updates and ignore relevant ARP updates. 132477d71233SIhar Hrachyshka */ 1325f0e0d044SVasily Khoruzhick if (new != old || lladdr != neigh->ha) 132677d71233SIhar Hrachyshka neigh->updated = jiffies; 132777d71233SIhar Hrachyshka 13281da177e4SLinus Torvalds if (new != old) { 13291da177e4SLinus Torvalds neigh_del_timer(neigh); 1330765c9c63SErik Kline if (new & NUD_PROBE) 1331765c9c63SErik Kline atomic_set(&neigh->probes, 0); 1332a43d8994SPavel Emelyanov if (new & NUD_IN_TIMER) 1333667347f1SDavid S. Miller neigh_add_timer(neigh, (jiffies + 13341da177e4SLinus Torvalds ((new & NUD_REACHABLE) ? 1335667347f1SDavid S. Miller neigh->parms->reachable_time : 1336667347f1SDavid S. Miller 0))); 13379c29a2f5SDavid Ahern neigh->nud_state = new; 133853385d2dSBob Gilligan notify = 1; 13391da177e4SLinus Torvalds } 13401da177e4SLinus Torvalds 13411da177e4SLinus Torvalds if (lladdr != neigh->ha) { 13420ed8ddf4SEric Dumazet write_seqlock(&neigh->ha_lock); 13431da177e4SLinus Torvalds memcpy(&neigh->ha, lladdr, dev->addr_len); 13440ed8ddf4SEric Dumazet write_sequnlock(&neigh->ha_lock); 13451da177e4SLinus Torvalds neigh_update_hhs(neigh); 13461da177e4SLinus Torvalds if (!(new & NUD_CONNECTED)) 13471da177e4SLinus Torvalds neigh->confirmed = jiffies - 13481f9248e5SJiri Pirko (NEIGH_VAR(neigh->parms, BASE_REACHABLE_TIME) << 1); 13491da177e4SLinus Torvalds notify = 1; 13501da177e4SLinus Torvalds } 13511da177e4SLinus Torvalds if (new == old) 13521da177e4SLinus Torvalds goto out; 13531da177e4SLinus Torvalds if (new & NUD_CONNECTED) 13541da177e4SLinus Torvalds neigh_connect(neigh); 13551da177e4SLinus Torvalds else 13561da177e4SLinus Torvalds neigh_suspect(neigh); 13571da177e4SLinus Torvalds if (!(old & NUD_VALID)) { 13581da177e4SLinus Torvalds struct sk_buff *skb; 13591da177e4SLinus Torvalds 13601da177e4SLinus Torvalds /* Again: avoid dead loop if something went wrong */ 13611da177e4SLinus Torvalds 13621da177e4SLinus Torvalds while (neigh->nud_state & NUD_VALID && 13631da177e4SLinus Torvalds (skb = __skb_dequeue(&neigh->arp_queue)) != NULL) { 136469cce1d1SDavid S. Miller struct dst_entry *dst = skb_dst(skb); 136569cce1d1SDavid S. Miller struct neighbour *n2, *n1 = neigh; 13661da177e4SLinus Torvalds write_unlock_bh(&neigh->lock); 1367e049f288Sroy.qing.li@gmail.com 1368e049f288Sroy.qing.li@gmail.com rcu_read_lock(); 136913a43d94SDavid S. Miller 137013a43d94SDavid S. Miller /* Why not just use 'neigh' as-is? The problem is that 137113a43d94SDavid S. Miller * things such as shaper, eql, and sch_teql can end up 137213a43d94SDavid S. Miller * using alternative, different, neigh objects to output 137313a43d94SDavid S. Miller * the packet in the output path. So what we need to do 137413a43d94SDavid S. Miller * here is re-lookup the top-level neigh in the path so 137513a43d94SDavid S. Miller * we can reinject the packet there. 137613a43d94SDavid S. Miller */ 137713a43d94SDavid S. Miller n2 = NULL; 137813a43d94SDavid S. Miller if (dst) { 137913a43d94SDavid S. Miller n2 = dst_neigh_lookup_skb(dst, skb); 138013a43d94SDavid S. Miller if (n2) 138169cce1d1SDavid S. Miller n1 = n2; 138213a43d94SDavid S. Miller } 13838f40b161SDavid S. Miller n1->output(n1, skb); 138413a43d94SDavid S. Miller if (n2) 138513a43d94SDavid S. Miller neigh_release(n2); 1386e049f288Sroy.qing.li@gmail.com rcu_read_unlock(); 1387e049f288Sroy.qing.li@gmail.com 13881da177e4SLinus Torvalds write_lock_bh(&neigh->lock); 13891da177e4SLinus Torvalds } 1390c9ab4d85SEric Dumazet __skb_queue_purge(&neigh->arp_queue); 13918b5c171bSEric Dumazet neigh->arp_queue_len_bytes = 0; 13921da177e4SLinus Torvalds } 13931da177e4SLinus Torvalds out: 1394fc6e8073SRoopa Prabhu if (update_isrouter) 1395fc6e8073SRoopa Prabhu neigh_update_is_router(neigh, flags, ¬ify); 13961da177e4SLinus Torvalds write_unlock_bh(&neigh->lock); 13978d71740cSTom Tucker 1398e997f8a2SDavid Ahern if (((new ^ old) & NUD_PERMANENT) || ext_learn_change) 13999c29a2f5SDavid Ahern neigh_update_gc_list(neigh); 14009c29a2f5SDavid Ahern 14018d71740cSTom Tucker if (notify) 14027b8f7a40SRoopa Prabhu neigh_update_notify(neigh, nlmsg_pid); 1403d961db35SThomas Graf 140456dd18a4SRoopa Prabhu trace_neigh_update_done(neigh, err); 140556dd18a4SRoopa Prabhu 14061da177e4SLinus Torvalds return err; 14071da177e4SLinus Torvalds } 14087a35a50dSDavid Ahern 14097a35a50dSDavid Ahern int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, 14107a35a50dSDavid Ahern u32 flags, u32 nlmsg_pid) 14117a35a50dSDavid Ahern { 14127a35a50dSDavid Ahern return __neigh_update(neigh, lladdr, new, flags, nlmsg_pid, NULL); 14137a35a50dSDavid Ahern } 14140a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_update); 14151da177e4SLinus Torvalds 14167e980569SJiri Benc /* Update the neigh to listen temporarily for probe responses, even if it is 14177e980569SJiri Benc * in a NUD_FAILED state. The caller has to hold neigh->lock for writing. 14187e980569SJiri Benc */ 14197e980569SJiri Benc void __neigh_set_probe_once(struct neighbour *neigh) 14207e980569SJiri Benc { 14212c51a97fSJulian Anastasov if (neigh->dead) 14222c51a97fSJulian Anastasov return; 14237e980569SJiri Benc neigh->updated = jiffies; 14247e980569SJiri Benc if (!(neigh->nud_state & NUD_FAILED)) 14257e980569SJiri Benc return; 14262176d5d4SDuan Jiong neigh->nud_state = NUD_INCOMPLETE; 14272176d5d4SDuan Jiong atomic_set(&neigh->probes, neigh_max_probes(neigh)); 14287e980569SJiri Benc neigh_add_timer(neigh, 14297e980569SJiri Benc jiffies + NEIGH_VAR(neigh->parms, RETRANS_TIME)); 14307e980569SJiri Benc } 14317e980569SJiri Benc EXPORT_SYMBOL(__neigh_set_probe_once); 14327e980569SJiri Benc 14331da177e4SLinus Torvalds struct neighbour *neigh_event_ns(struct neigh_table *tbl, 14341da177e4SLinus Torvalds u8 *lladdr, void *saddr, 14351da177e4SLinus Torvalds struct net_device *dev) 14361da177e4SLinus Torvalds { 14371da177e4SLinus Torvalds struct neighbour *neigh = __neigh_lookup(tbl, saddr, dev, 14381da177e4SLinus Torvalds lladdr || !dev->addr_len); 14391da177e4SLinus Torvalds if (neigh) 14401da177e4SLinus Torvalds neigh_update(neigh, lladdr, NUD_STALE, 14417b8f7a40SRoopa Prabhu NEIGH_UPDATE_F_OVERRIDE, 0); 14421da177e4SLinus Torvalds return neigh; 14431da177e4SLinus Torvalds } 14440a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_event_ns); 14451da177e4SLinus Torvalds 144634d101ddSEric Dumazet /* called with read_lock_bh(&n->lock); */ 1447bdf53c58SEric W. Biederman static void neigh_hh_init(struct neighbour *n) 14481da177e4SLinus Torvalds { 1449bdf53c58SEric W. Biederman struct net_device *dev = n->dev; 1450bdf53c58SEric W. Biederman __be16 prot = n->tbl->protocol; 1451f6b72b62SDavid S. Miller struct hh_cache *hh = &n->hh; 14520ed8ddf4SEric Dumazet 14530ed8ddf4SEric Dumazet write_lock_bh(&n->lock); 145434d101ddSEric Dumazet 1455f6b72b62SDavid S. Miller /* Only one thread can come in here and initialize the 1456f6b72b62SDavid S. Miller * hh_cache entry. 1457f6b72b62SDavid S. Miller */ 1458b23b5455SDavid S. Miller if (!hh->hh_len) 1459b23b5455SDavid S. Miller dev->header_ops->cache(n, hh, prot); 1460f6b72b62SDavid S. Miller 14610ed8ddf4SEric Dumazet write_unlock_bh(&n->lock); 14621da177e4SLinus Torvalds } 14631da177e4SLinus Torvalds 14641da177e4SLinus Torvalds /* Slow and careful. */ 14651da177e4SLinus Torvalds 14668f40b161SDavid S. Miller int neigh_resolve_output(struct neighbour *neigh, struct sk_buff *skb) 14671da177e4SLinus Torvalds { 14681da177e4SLinus Torvalds int rc = 0; 14691da177e4SLinus Torvalds 14701da177e4SLinus Torvalds if (!neigh_event_send(neigh, skb)) { 14711da177e4SLinus Torvalds int err; 14721da177e4SLinus Torvalds struct net_device *dev = neigh->dev; 14730ed8ddf4SEric Dumazet unsigned int seq; 147434d101ddSEric Dumazet 1475f6b72b62SDavid S. Miller if (dev->header_ops->cache && !neigh->hh.hh_len) 1476bdf53c58SEric W. Biederman neigh_hh_init(neigh); 147734d101ddSEric Dumazet 14780ed8ddf4SEric Dumazet do { 1479e1f16503Sramesh.nagappa@gmail.com __skb_pull(skb, skb_network_offset(skb)); 14800ed8ddf4SEric Dumazet seq = read_seqbegin(&neigh->ha_lock); 14810c4e8581SStephen Hemminger err = dev_hard_header(skb, dev, ntohs(skb->protocol), 14821da177e4SLinus Torvalds neigh->ha, NULL, skb->len); 14830ed8ddf4SEric Dumazet } while (read_seqretry(&neigh->ha_lock, seq)); 148434d101ddSEric Dumazet 14851da177e4SLinus Torvalds if (err >= 0) 1486542d4d68SDavid S. Miller rc = dev_queue_xmit(skb); 14871da177e4SLinus Torvalds else 14881da177e4SLinus Torvalds goto out_kfree_skb; 14891da177e4SLinus Torvalds } 14901da177e4SLinus Torvalds out: 14911da177e4SLinus Torvalds return rc; 14921da177e4SLinus Torvalds out_kfree_skb: 14931da177e4SLinus Torvalds rc = -EINVAL; 14941da177e4SLinus Torvalds kfree_skb(skb); 14951da177e4SLinus Torvalds goto out; 14961da177e4SLinus Torvalds } 14970a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_resolve_output); 14981da177e4SLinus Torvalds 14991da177e4SLinus Torvalds /* As fast as possible without hh cache */ 15001da177e4SLinus Torvalds 15018f40b161SDavid S. Miller int neigh_connected_output(struct neighbour *neigh, struct sk_buff *skb) 15021da177e4SLinus Torvalds { 15031da177e4SLinus Torvalds struct net_device *dev = neigh->dev; 15040ed8ddf4SEric Dumazet unsigned int seq; 15058f40b161SDavid S. Miller int err; 15061da177e4SLinus Torvalds 15070ed8ddf4SEric Dumazet do { 1508e1f16503Sramesh.nagappa@gmail.com __skb_pull(skb, skb_network_offset(skb)); 15090ed8ddf4SEric Dumazet seq = read_seqbegin(&neigh->ha_lock); 15100c4e8581SStephen Hemminger err = dev_hard_header(skb, dev, ntohs(skb->protocol), 15111da177e4SLinus Torvalds neigh->ha, NULL, skb->len); 15120ed8ddf4SEric Dumazet } while (read_seqretry(&neigh->ha_lock, seq)); 15130ed8ddf4SEric Dumazet 15141da177e4SLinus Torvalds if (err >= 0) 1515542d4d68SDavid S. Miller err = dev_queue_xmit(skb); 15161da177e4SLinus Torvalds else { 15171da177e4SLinus Torvalds err = -EINVAL; 15181da177e4SLinus Torvalds kfree_skb(skb); 15191da177e4SLinus Torvalds } 15201da177e4SLinus Torvalds return err; 15211da177e4SLinus Torvalds } 15220a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_connected_output); 15231da177e4SLinus Torvalds 15248f40b161SDavid S. Miller int neigh_direct_output(struct neighbour *neigh, struct sk_buff *skb) 15258f40b161SDavid S. Miller { 15268f40b161SDavid S. Miller return dev_queue_xmit(skb); 15278f40b161SDavid S. Miller } 15288f40b161SDavid S. Miller EXPORT_SYMBOL(neigh_direct_output); 15298f40b161SDavid S. Miller 1530e99e88a9SKees Cook static void neigh_proxy_process(struct timer_list *t) 15311da177e4SLinus Torvalds { 1532e99e88a9SKees Cook struct neigh_table *tbl = from_timer(tbl, t, proxy_timer); 15331da177e4SLinus Torvalds long sched_next = 0; 15341da177e4SLinus Torvalds unsigned long now = jiffies; 1535f72051b0SDavid S. Miller struct sk_buff *skb, *n; 15361da177e4SLinus Torvalds 15371da177e4SLinus Torvalds spin_lock(&tbl->proxy_queue.lock); 15381da177e4SLinus Torvalds 1539f72051b0SDavid S. Miller skb_queue_walk_safe(&tbl->proxy_queue, skb, n) { 1540f72051b0SDavid S. Miller long tdif = NEIGH_CB(skb)->sched_next - now; 15411da177e4SLinus Torvalds 15421da177e4SLinus Torvalds if (tdif <= 0) { 1543f72051b0SDavid S. Miller struct net_device *dev = skb->dev; 154420e6074eSEric Dumazet 1545f72051b0SDavid S. Miller __skb_unlink(skb, &tbl->proxy_queue); 154620e6074eSEric Dumazet if (tbl->proxy_redo && netif_running(dev)) { 154720e6074eSEric Dumazet rcu_read_lock(); 1548f72051b0SDavid S. Miller tbl->proxy_redo(skb); 154920e6074eSEric Dumazet rcu_read_unlock(); 155020e6074eSEric Dumazet } else { 1551f72051b0SDavid S. Miller kfree_skb(skb); 155220e6074eSEric Dumazet } 15531da177e4SLinus Torvalds 15541da177e4SLinus Torvalds dev_put(dev); 15551da177e4SLinus Torvalds } else if (!sched_next || tdif < sched_next) 15561da177e4SLinus Torvalds sched_next = tdif; 15571da177e4SLinus Torvalds } 15581da177e4SLinus Torvalds del_timer(&tbl->proxy_timer); 15591da177e4SLinus Torvalds if (sched_next) 15601da177e4SLinus Torvalds mod_timer(&tbl->proxy_timer, jiffies + sched_next); 15611da177e4SLinus Torvalds spin_unlock(&tbl->proxy_queue.lock); 15621da177e4SLinus Torvalds } 15631da177e4SLinus Torvalds 15641da177e4SLinus Torvalds void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p, 15651da177e4SLinus Torvalds struct sk_buff *skb) 15661da177e4SLinus Torvalds { 15671da177e4SLinus Torvalds unsigned long now = jiffies; 156863862b5bSAruna-Hewapathirane 156963862b5bSAruna-Hewapathirane unsigned long sched_next = now + (prandom_u32() % 15701f9248e5SJiri Pirko NEIGH_VAR(p, PROXY_DELAY)); 15711da177e4SLinus Torvalds 15721f9248e5SJiri Pirko if (tbl->proxy_queue.qlen > NEIGH_VAR(p, PROXY_QLEN)) { 15731da177e4SLinus Torvalds kfree_skb(skb); 15741da177e4SLinus Torvalds return; 15751da177e4SLinus Torvalds } 1576a61bbcf2SPatrick McHardy 1577a61bbcf2SPatrick McHardy NEIGH_CB(skb)->sched_next = sched_next; 1578a61bbcf2SPatrick McHardy NEIGH_CB(skb)->flags |= LOCALLY_ENQUEUED; 15791da177e4SLinus Torvalds 15801da177e4SLinus Torvalds spin_lock(&tbl->proxy_queue.lock); 15811da177e4SLinus Torvalds if (del_timer(&tbl->proxy_timer)) { 15821da177e4SLinus Torvalds if (time_before(tbl->proxy_timer.expires, sched_next)) 15831da177e4SLinus Torvalds sched_next = tbl->proxy_timer.expires; 15841da177e4SLinus Torvalds } 1585adf30907SEric Dumazet skb_dst_drop(skb); 15861da177e4SLinus Torvalds dev_hold(skb->dev); 15871da177e4SLinus Torvalds __skb_queue_tail(&tbl->proxy_queue, skb); 15881da177e4SLinus Torvalds mod_timer(&tbl->proxy_timer, sched_next); 15891da177e4SLinus Torvalds spin_unlock(&tbl->proxy_queue.lock); 15901da177e4SLinus Torvalds } 15910a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(pneigh_enqueue); 15921da177e4SLinus Torvalds 159397fd5bc7STobias Klauser static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl, 1594426b5303SEric W. Biederman struct net *net, int ifindex) 1595426b5303SEric W. Biederman { 1596426b5303SEric W. Biederman struct neigh_parms *p; 1597426b5303SEric W. Biederman 159875fbfd33SNicolas Dichtel list_for_each_entry(p, &tbl->parms_list, list) { 1599878628fbSYOSHIFUJI Hideaki if ((p->dev && p->dev->ifindex == ifindex && net_eq(neigh_parms_net(p), net)) || 1600170d6f99SGao feng (!p->dev && !ifindex && net_eq(net, &init_net))) 1601426b5303SEric W. Biederman return p; 1602426b5303SEric W. Biederman } 1603426b5303SEric W. Biederman 1604426b5303SEric W. Biederman return NULL; 1605426b5303SEric W. Biederman } 16061da177e4SLinus Torvalds 16071da177e4SLinus Torvalds struct neigh_parms *neigh_parms_alloc(struct net_device *dev, 16081da177e4SLinus Torvalds struct neigh_table *tbl) 16091da177e4SLinus Torvalds { 1610cf89d6b2SGao feng struct neigh_parms *p; 161100829823SStephen Hemminger struct net *net = dev_net(dev); 161200829823SStephen Hemminger const struct net_device_ops *ops = dev->netdev_ops; 16131da177e4SLinus Torvalds 1614cf89d6b2SGao feng p = kmemdup(&tbl->parms, sizeof(*p), GFP_KERNEL); 16151da177e4SLinus Torvalds if (p) { 16161da177e4SLinus Torvalds p->tbl = tbl; 16176343944bSReshetova, Elena refcount_set(&p->refcnt, 1); 16181da177e4SLinus Torvalds p->reachable_time = 16191f9248e5SJiri Pirko neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); 1620c7fb64dbSThomas Graf dev_hold(dev); 1621c7fb64dbSThomas Graf p->dev = dev; 1622efd7ef1cSEric W. Biederman write_pnet(&p->net, net); 16231da177e4SLinus Torvalds p->sysctl_table = NULL; 162463134803SVeaceslav Falico 162563134803SVeaceslav Falico if (ops->ndo_neigh_setup && ops->ndo_neigh_setup(dev, p)) { 162663134803SVeaceslav Falico dev_put(dev); 162763134803SVeaceslav Falico kfree(p); 162863134803SVeaceslav Falico return NULL; 162963134803SVeaceslav Falico } 163063134803SVeaceslav Falico 16311da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 163275fbfd33SNicolas Dichtel list_add(&p->list, &tbl->parms.list); 16331da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 16341d4c8c29SJiri Pirko 16351d4c8c29SJiri Pirko neigh_parms_data_state_cleanall(p); 16361da177e4SLinus Torvalds } 16371da177e4SLinus Torvalds return p; 16381da177e4SLinus Torvalds } 16390a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_parms_alloc); 16401da177e4SLinus Torvalds 16411da177e4SLinus Torvalds static void neigh_rcu_free_parms(struct rcu_head *head) 16421da177e4SLinus Torvalds { 16431da177e4SLinus Torvalds struct neigh_parms *parms = 16441da177e4SLinus Torvalds container_of(head, struct neigh_parms, rcu_head); 16451da177e4SLinus Torvalds 16461da177e4SLinus Torvalds neigh_parms_put(parms); 16471da177e4SLinus Torvalds } 16481da177e4SLinus Torvalds 16491da177e4SLinus Torvalds void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms) 16501da177e4SLinus Torvalds { 16511da177e4SLinus Torvalds if (!parms || parms == &tbl->parms) 16521da177e4SLinus Torvalds return; 16531da177e4SLinus Torvalds write_lock_bh(&tbl->lock); 165475fbfd33SNicolas Dichtel list_del(&parms->list); 16551da177e4SLinus Torvalds parms->dead = 1; 16561da177e4SLinus Torvalds write_unlock_bh(&tbl->lock); 1657cecbb639SDavid S. Miller if (parms->dev) 1658cecbb639SDavid S. Miller dev_put(parms->dev); 16591da177e4SLinus Torvalds call_rcu(&parms->rcu_head, neigh_rcu_free_parms); 16601da177e4SLinus Torvalds } 16610a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_parms_release); 16621da177e4SLinus Torvalds 166306f0511dSDenis V. Lunev static void neigh_parms_destroy(struct neigh_parms *parms) 16641da177e4SLinus Torvalds { 16651da177e4SLinus Torvalds kfree(parms); 16661da177e4SLinus Torvalds } 16671da177e4SLinus Torvalds 1668c2ecba71SPavel Emelianov static struct lock_class_key neigh_table_proxy_queue_class; 1669c2ecba71SPavel Emelianov 1670d7480fd3SWANG Cong static struct neigh_table *neigh_tables[NEIGH_NR_TABLES] __read_mostly; 1671d7480fd3SWANG Cong 1672d7480fd3SWANG Cong void neigh_table_init(int index, struct neigh_table *tbl) 16731da177e4SLinus Torvalds { 16741da177e4SLinus Torvalds unsigned long now = jiffies; 16751da177e4SLinus Torvalds unsigned long phsize; 16761da177e4SLinus Torvalds 167775fbfd33SNicolas Dichtel INIT_LIST_HEAD(&tbl->parms_list); 167858956317SDavid Ahern INIT_LIST_HEAD(&tbl->gc_list); 167975fbfd33SNicolas Dichtel list_add(&tbl->parms.list, &tbl->parms_list); 1680e42ea986SEric Dumazet write_pnet(&tbl->parms.net, &init_net); 16816343944bSReshetova, Elena refcount_set(&tbl->parms.refcnt, 1); 16821da177e4SLinus Torvalds tbl->parms.reachable_time = 16831f9248e5SJiri Pirko neigh_rand_reach_time(NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME)); 16841da177e4SLinus Torvalds 16851da177e4SLinus Torvalds tbl->stats = alloc_percpu(struct neigh_statistics); 16861da177e4SLinus Torvalds if (!tbl->stats) 16871da177e4SLinus Torvalds panic("cannot create neighbour cache statistics"); 16881da177e4SLinus Torvalds 16891da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 169071a5053aSChristoph Hellwig if (!proc_create_seq_data(tbl->id, 0, init_net.proc_net_stat, 169171a5053aSChristoph Hellwig &neigh_stat_seq_ops, tbl)) 16921da177e4SLinus Torvalds panic("cannot create neighbour proc dir entry"); 16931da177e4SLinus Torvalds #endif 16941da177e4SLinus Torvalds 1695cd089336SDavid S. Miller RCU_INIT_POINTER(tbl->nht, neigh_hash_alloc(3)); 16961da177e4SLinus Torvalds 16971da177e4SLinus Torvalds phsize = (PNEIGH_HASHMASK + 1) * sizeof(struct pneigh_entry *); 169877d04bd9SAndrew Morton tbl->phash_buckets = kzalloc(phsize, GFP_KERNEL); 16991da177e4SLinus Torvalds 1700d6bf7817SEric Dumazet if (!tbl->nht || !tbl->phash_buckets) 17011da177e4SLinus Torvalds panic("cannot allocate neighbour cache hashes"); 17021da177e4SLinus Torvalds 170308433effSYOSHIFUJI Hideaki / 吉藤英明 if (!tbl->entry_size) 170408433effSYOSHIFUJI Hideaki / 吉藤英明 tbl->entry_size = ALIGN(offsetof(struct neighbour, primary_key) + 170508433effSYOSHIFUJI Hideaki / 吉藤英明 tbl->key_len, NEIGH_PRIV_ALIGN); 170608433effSYOSHIFUJI Hideaki / 吉藤英明 else 170708433effSYOSHIFUJI Hideaki / 吉藤英明 WARN_ON(tbl->entry_size % NEIGH_PRIV_ALIGN); 170808433effSYOSHIFUJI Hideaki / 吉藤英明 17091da177e4SLinus Torvalds rwlock_init(&tbl->lock); 1710203b42f7STejun Heo INIT_DEFERRABLE_WORK(&tbl->gc_work, neigh_periodic_work); 1711f618002bSviresh kumar queue_delayed_work(system_power_efficient_wq, &tbl->gc_work, 1712f618002bSviresh kumar tbl->parms.reachable_time); 1713e99e88a9SKees Cook timer_setup(&tbl->proxy_timer, neigh_proxy_process, 0); 1714c2ecba71SPavel Emelianov skb_queue_head_init_class(&tbl->proxy_queue, 1715c2ecba71SPavel Emelianov &neigh_table_proxy_queue_class); 17161da177e4SLinus Torvalds 17171da177e4SLinus Torvalds tbl->last_flush = now; 17181da177e4SLinus Torvalds tbl->last_rand = now + tbl->parms.reachable_time * 20; 1719bd89efc5SSimon Kelley 1720d7480fd3SWANG Cong neigh_tables[index] = tbl; 17211da177e4SLinus Torvalds } 17220a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_table_init); 17231da177e4SLinus Torvalds 1724d7480fd3SWANG Cong int neigh_table_clear(int index, struct neigh_table *tbl) 17251da177e4SLinus Torvalds { 1726d7480fd3SWANG Cong neigh_tables[index] = NULL; 17271da177e4SLinus Torvalds /* It is not clean... Fix it to unload IPv6 module safely */ 1728a5c30b34STejun Heo cancel_delayed_work_sync(&tbl->gc_work); 17291da177e4SLinus Torvalds del_timer_sync(&tbl->proxy_timer); 17301da177e4SLinus Torvalds pneigh_queue_purge(&tbl->proxy_queue); 17311da177e4SLinus Torvalds neigh_ifdown(tbl, NULL); 17321da177e4SLinus Torvalds if (atomic_read(&tbl->entries)) 1733e005d193SJoe Perches pr_crit("neighbour leakage\n"); 17341da177e4SLinus Torvalds 17356193d2beSEric Dumazet call_rcu(&rcu_dereference_protected(tbl->nht, 1)->rcu, 17366193d2beSEric Dumazet neigh_hash_free_rcu); 1737d6bf7817SEric Dumazet tbl->nht = NULL; 17381da177e4SLinus Torvalds 17391da177e4SLinus Torvalds kfree(tbl->phash_buckets); 17401da177e4SLinus Torvalds tbl->phash_buckets = NULL; 17411da177e4SLinus Torvalds 17423f192b5cSAlexey Dobriyan remove_proc_entry(tbl->id, init_net.proc_net_stat); 17433f192b5cSAlexey Dobriyan 17443fcde74bSKirill Korotaev free_percpu(tbl->stats); 17453fcde74bSKirill Korotaev tbl->stats = NULL; 17463fcde74bSKirill Korotaev 17471da177e4SLinus Torvalds return 0; 17481da177e4SLinus Torvalds } 17490a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_table_clear); 17501da177e4SLinus Torvalds 1751d7480fd3SWANG Cong static struct neigh_table *neigh_find_table(int family) 1752d7480fd3SWANG Cong { 1753d7480fd3SWANG Cong struct neigh_table *tbl = NULL; 1754d7480fd3SWANG Cong 1755d7480fd3SWANG Cong switch (family) { 1756d7480fd3SWANG Cong case AF_INET: 1757d7480fd3SWANG Cong tbl = neigh_tables[NEIGH_ARP_TABLE]; 1758d7480fd3SWANG Cong break; 1759d7480fd3SWANG Cong case AF_INET6: 1760d7480fd3SWANG Cong tbl = neigh_tables[NEIGH_ND_TABLE]; 1761d7480fd3SWANG Cong break; 1762d7480fd3SWANG Cong case AF_DECnet: 1763d7480fd3SWANG Cong tbl = neigh_tables[NEIGH_DN_TABLE]; 1764d7480fd3SWANG Cong break; 1765d7480fd3SWANG Cong } 1766d7480fd3SWANG Cong 1767d7480fd3SWANG Cong return tbl; 1768d7480fd3SWANG Cong } 1769d7480fd3SWANG Cong 177082cbb5c6SRoopa Prabhu const struct nla_policy nda_policy[NDA_MAX+1] = { 177182cbb5c6SRoopa Prabhu [NDA_DST] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, 177282cbb5c6SRoopa Prabhu [NDA_LLADDR] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, 177382cbb5c6SRoopa Prabhu [NDA_CACHEINFO] = { .len = sizeof(struct nda_cacheinfo) }, 177482cbb5c6SRoopa Prabhu [NDA_PROBES] = { .type = NLA_U32 }, 177582cbb5c6SRoopa Prabhu [NDA_VLAN] = { .type = NLA_U16 }, 177682cbb5c6SRoopa Prabhu [NDA_PORT] = { .type = NLA_U16 }, 177782cbb5c6SRoopa Prabhu [NDA_VNI] = { .type = NLA_U32 }, 177882cbb5c6SRoopa Prabhu [NDA_IFINDEX] = { .type = NLA_U32 }, 177982cbb5c6SRoopa Prabhu [NDA_MASTER] = { .type = NLA_U32 }, 1780a9cd3439SDavid Ahern [NDA_PROTOCOL] = { .type = NLA_U8 }, 178182cbb5c6SRoopa Prabhu }; 178282cbb5c6SRoopa Prabhu 1783c21ef3e3SDavid Ahern static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, 1784c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 17851da177e4SLinus Torvalds { 17863b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1787a14a49d2SThomas Graf struct ndmsg *ndm; 1788a14a49d2SThomas Graf struct nlattr *dst_attr; 17891da177e4SLinus Torvalds struct neigh_table *tbl; 1790d7480fd3SWANG Cong struct neighbour *neigh; 17911da177e4SLinus Torvalds struct net_device *dev = NULL; 1792a14a49d2SThomas Graf int err = -EINVAL; 17931da177e4SLinus Torvalds 1794110b2499SEric Dumazet ASSERT_RTNL(); 1795a14a49d2SThomas Graf if (nlmsg_len(nlh) < sizeof(*ndm)) 17961da177e4SLinus Torvalds goto out; 17971da177e4SLinus Torvalds 1798a14a49d2SThomas Graf dst_attr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_DST); 17997a35a50dSDavid Ahern if (!dst_attr) { 18007a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Network address not specified"); 1801a14a49d2SThomas Graf goto out; 18027a35a50dSDavid Ahern } 1803a14a49d2SThomas Graf 1804a14a49d2SThomas Graf ndm = nlmsg_data(nlh); 1805a14a49d2SThomas Graf if (ndm->ndm_ifindex) { 1806110b2499SEric Dumazet dev = __dev_get_by_index(net, ndm->ndm_ifindex); 1807a14a49d2SThomas Graf if (dev == NULL) { 1808a14a49d2SThomas Graf err = -ENODEV; 1809a14a49d2SThomas Graf goto out; 1810a14a49d2SThomas Graf } 1811a14a49d2SThomas Graf } 1812a14a49d2SThomas Graf 1813d7480fd3SWANG Cong tbl = neigh_find_table(ndm->ndm_family); 1814d7480fd3SWANG Cong if (tbl == NULL) 1815d7480fd3SWANG Cong return -EAFNOSUPPORT; 18161da177e4SLinus Torvalds 18177a35a50dSDavid Ahern if (nla_len(dst_attr) < (int)tbl->key_len) { 18187a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid network address"); 1819110b2499SEric Dumazet goto out; 18207a35a50dSDavid Ahern } 18211da177e4SLinus Torvalds 18221da177e4SLinus Torvalds if (ndm->ndm_flags & NTF_PROXY) { 1823426b5303SEric W. Biederman err = pneigh_delete(tbl, net, nla_data(dst_attr), dev); 1824110b2499SEric Dumazet goto out; 18251da177e4SLinus Torvalds } 18261da177e4SLinus Torvalds 1827a14a49d2SThomas Graf if (dev == NULL) 1828110b2499SEric Dumazet goto out; 18291da177e4SLinus Torvalds 1830a14a49d2SThomas Graf neigh = neigh_lookup(tbl, nla_data(dst_attr), dev); 1831a14a49d2SThomas Graf if (neigh == NULL) { 1832a14a49d2SThomas Graf err = -ENOENT; 1833110b2499SEric Dumazet goto out; 1834a14a49d2SThomas Graf } 1835a14a49d2SThomas Graf 18367a35a50dSDavid Ahern err = __neigh_update(neigh, NULL, NUD_FAILED, 18377a35a50dSDavid Ahern NEIGH_UPDATE_F_OVERRIDE | NEIGH_UPDATE_F_ADMIN, 18387a35a50dSDavid Ahern NETLINK_CB(skb).portid, extack); 18395071034eSSowmini Varadhan write_lock_bh(&tbl->lock); 1840a14a49d2SThomas Graf neigh_release(neigh); 18415071034eSSowmini Varadhan neigh_remove_one(neigh, tbl); 18425071034eSSowmini Varadhan write_unlock_bh(&tbl->lock); 1843a14a49d2SThomas Graf 18441da177e4SLinus Torvalds out: 18451da177e4SLinus Torvalds return err; 18461da177e4SLinus Torvalds } 18471da177e4SLinus Torvalds 1848c21ef3e3SDavid Ahern static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, 1849c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 18501da177e4SLinus Torvalds { 1851f7aa74e4SRoopa Prabhu int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE | 1852f7aa74e4SRoopa Prabhu NEIGH_UPDATE_F_OVERRIDE_ISROUTER; 18533b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 18545208debdSThomas Graf struct ndmsg *ndm; 18555208debdSThomas Graf struct nlattr *tb[NDA_MAX+1]; 18561da177e4SLinus Torvalds struct neigh_table *tbl; 18571da177e4SLinus Torvalds struct net_device *dev = NULL; 1858d7480fd3SWANG Cong struct neighbour *neigh; 1859d7480fd3SWANG Cong void *dst, *lladdr; 1860df9b0e30SDavid Ahern u8 protocol = 0; 18615208debdSThomas Graf int err; 18621da177e4SLinus Torvalds 1863110b2499SEric Dumazet ASSERT_RTNL(); 18648cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(*ndm), tb, NDA_MAX, 18658cb08174SJohannes Berg nda_policy, extack); 18665208debdSThomas Graf if (err < 0) 18671da177e4SLinus Torvalds goto out; 18681da177e4SLinus Torvalds 18695208debdSThomas Graf err = -EINVAL; 18707a35a50dSDavid Ahern if (!tb[NDA_DST]) { 18717a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Network address not specified"); 18725208debdSThomas Graf goto out; 18737a35a50dSDavid Ahern } 18745208debdSThomas Graf 18755208debdSThomas Graf ndm = nlmsg_data(nlh); 18765208debdSThomas Graf if (ndm->ndm_ifindex) { 1877110b2499SEric Dumazet dev = __dev_get_by_index(net, ndm->ndm_ifindex); 18785208debdSThomas Graf if (dev == NULL) { 18795208debdSThomas Graf err = -ENODEV; 18805208debdSThomas Graf goto out; 18815208debdSThomas Graf } 18825208debdSThomas Graf 18837a35a50dSDavid Ahern if (tb[NDA_LLADDR] && nla_len(tb[NDA_LLADDR]) < dev->addr_len) { 18847a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid link address"); 1885110b2499SEric Dumazet goto out; 18865208debdSThomas Graf } 18877a35a50dSDavid Ahern } 18885208debdSThomas Graf 1889d7480fd3SWANG Cong tbl = neigh_find_table(ndm->ndm_family); 1890d7480fd3SWANG Cong if (tbl == NULL) 1891d7480fd3SWANG Cong return -EAFNOSUPPORT; 18921da177e4SLinus Torvalds 18937a35a50dSDavid Ahern if (nla_len(tb[NDA_DST]) < (int)tbl->key_len) { 18947a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid network address"); 1895110b2499SEric Dumazet goto out; 18967a35a50dSDavid Ahern } 18977a35a50dSDavid Ahern 18985208debdSThomas Graf dst = nla_data(tb[NDA_DST]); 18995208debdSThomas Graf lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL; 19001da177e4SLinus Torvalds 1901a9cd3439SDavid Ahern if (tb[NDA_PROTOCOL]) 1902df9b0e30SDavid Ahern protocol = nla_get_u8(tb[NDA_PROTOCOL]); 1903df9b0e30SDavid Ahern 19041da177e4SLinus Torvalds if (ndm->ndm_flags & NTF_PROXY) { 190562dd9318SVille Nuorvala struct pneigh_entry *pn; 190662dd9318SVille Nuorvala 19075208debdSThomas Graf err = -ENOBUFS; 1908426b5303SEric W. Biederman pn = pneigh_lookup(tbl, net, dst, dev, 1); 190962dd9318SVille Nuorvala if (pn) { 191062dd9318SVille Nuorvala pn->flags = ndm->ndm_flags; 1911df9b0e30SDavid Ahern if (protocol) 1912df9b0e30SDavid Ahern pn->protocol = protocol; 191362dd9318SVille Nuorvala err = 0; 191462dd9318SVille Nuorvala } 1915110b2499SEric Dumazet goto out; 19161da177e4SLinus Torvalds } 19171da177e4SLinus Torvalds 19187a35a50dSDavid Ahern if (!dev) { 19197a35a50dSDavid Ahern NL_SET_ERR_MSG(extack, "Device not specified"); 1920110b2499SEric Dumazet goto out; 19217a35a50dSDavid Ahern } 19221da177e4SLinus Torvalds 1923b8fb1ab4SDavid Ahern if (tbl->allow_add && !tbl->allow_add(dev, extack)) { 1924b8fb1ab4SDavid Ahern err = -EINVAL; 1925b8fb1ab4SDavid Ahern goto out; 1926b8fb1ab4SDavid Ahern } 1927b8fb1ab4SDavid Ahern 19285208debdSThomas Graf neigh = neigh_lookup(tbl, dst, dev); 19295208debdSThomas Graf if (neigh == NULL) { 1930e997f8a2SDavid Ahern bool exempt_from_gc; 1931e997f8a2SDavid Ahern 19325208debdSThomas Graf if (!(nlh->nlmsg_flags & NLM_F_CREATE)) { 19331da177e4SLinus Torvalds err = -ENOENT; 1934110b2499SEric Dumazet goto out; 19355208debdSThomas Graf } 19365208debdSThomas Graf 1937e997f8a2SDavid Ahern exempt_from_gc = ndm->ndm_state & NUD_PERMANENT || 1938e997f8a2SDavid Ahern ndm->ndm_flags & NTF_EXT_LEARNED; 1939e997f8a2SDavid Ahern neigh = ___neigh_create(tbl, dst, dev, exempt_from_gc, true); 19405208debdSThomas Graf if (IS_ERR(neigh)) { 19415208debdSThomas Graf err = PTR_ERR(neigh); 1942110b2499SEric Dumazet goto out; 19431da177e4SLinus Torvalds } 19445208debdSThomas Graf } else { 19455208debdSThomas Graf if (nlh->nlmsg_flags & NLM_F_EXCL) { 19465208debdSThomas Graf err = -EEXIST; 19475208debdSThomas Graf neigh_release(neigh); 1948110b2499SEric Dumazet goto out; 19491da177e4SLinus Torvalds } 19501da177e4SLinus Torvalds 19515208debdSThomas Graf if (!(nlh->nlmsg_flags & NLM_F_REPLACE)) 1952f7aa74e4SRoopa Prabhu flags &= ~(NEIGH_UPDATE_F_OVERRIDE | 1953f7aa74e4SRoopa Prabhu NEIGH_UPDATE_F_OVERRIDE_ISROUTER); 19545208debdSThomas Graf } 19551da177e4SLinus Torvalds 19569ce33e46SRoopa Prabhu if (ndm->ndm_flags & NTF_EXT_LEARNED) 19579ce33e46SRoopa Prabhu flags |= NEIGH_UPDATE_F_EXT_LEARNED; 19589ce33e46SRoopa Prabhu 1959f7aa74e4SRoopa Prabhu if (ndm->ndm_flags & NTF_ROUTER) 1960f7aa74e4SRoopa Prabhu flags |= NEIGH_UPDATE_F_ISROUTER; 1961f7aa74e4SRoopa Prabhu 19620c5c2d30SEric Biederman if (ndm->ndm_flags & NTF_USE) { 19630c5c2d30SEric Biederman neigh_event_send(neigh, NULL); 19640c5c2d30SEric Biederman err = 0; 19650c5c2d30SEric Biederman } else 19667a35a50dSDavid Ahern err = __neigh_update(neigh, lladdr, ndm->ndm_state, flags, 19677a35a50dSDavid Ahern NETLINK_CB(skb).portid, extack); 1968df9b0e30SDavid Ahern 1969df9b0e30SDavid Ahern if (protocol) 1970df9b0e30SDavid Ahern neigh->protocol = protocol; 1971df9b0e30SDavid Ahern 19725208debdSThomas Graf neigh_release(neigh); 19731da177e4SLinus Torvalds 19741da177e4SLinus Torvalds out: 19751da177e4SLinus Torvalds return err; 19761da177e4SLinus Torvalds } 19771da177e4SLinus Torvalds 1978c7fb64dbSThomas Graf static int neightbl_fill_parms(struct sk_buff *skb, struct neigh_parms *parms) 1979c7fb64dbSThomas Graf { 1980ca860fb3SThomas Graf struct nlattr *nest; 1981e386c6ebSThomas Graf 1982ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, NDTA_PARMS); 1983ca860fb3SThomas Graf if (nest == NULL) 1984ca860fb3SThomas Graf return -ENOBUFS; 1985c7fb64dbSThomas Graf 19869a6308d7SDavid S. Miller if ((parms->dev && 19879a6308d7SDavid S. Miller nla_put_u32(skb, NDTPA_IFINDEX, parms->dev->ifindex)) || 19886343944bSReshetova, Elena nla_put_u32(skb, NDTPA_REFCNT, refcount_read(&parms->refcnt)) || 19891f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_QUEUE_LENBYTES, 19901f9248e5SJiri Pirko NEIGH_VAR(parms, QUEUE_LEN_BYTES)) || 19918b5c171bSEric Dumazet /* approximative value for deprecated QUEUE_LEN (in packets) */ 19929a6308d7SDavid S. Miller nla_put_u32(skb, NDTPA_QUEUE_LEN, 19931f9248e5SJiri Pirko NEIGH_VAR(parms, QUEUE_LEN_BYTES) / SKB_TRUESIZE(ETH_FRAME_LEN)) || 19941f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_PROXY_QLEN, NEIGH_VAR(parms, PROXY_QLEN)) || 19951f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_APP_PROBES, NEIGH_VAR(parms, APP_PROBES)) || 19961f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_UCAST_PROBES, 19971f9248e5SJiri Pirko NEIGH_VAR(parms, UCAST_PROBES)) || 19981f9248e5SJiri Pirko nla_put_u32(skb, NDTPA_MCAST_PROBES, 19991f9248e5SJiri Pirko NEIGH_VAR(parms, MCAST_PROBES)) || 20008da86466SYOSHIFUJI Hideaki/吉藤英明 nla_put_u32(skb, NDTPA_MCAST_REPROBES, 20018da86466SYOSHIFUJI Hideaki/吉藤英明 NEIGH_VAR(parms, MCAST_REPROBES)) || 20022175d87cSNicolas Dichtel nla_put_msecs(skb, NDTPA_REACHABLE_TIME, parms->reachable_time, 20032175d87cSNicolas Dichtel NDTPA_PAD) || 20049a6308d7SDavid S. Miller nla_put_msecs(skb, NDTPA_BASE_REACHABLE_TIME, 20052175d87cSNicolas Dichtel NEIGH_VAR(parms, BASE_REACHABLE_TIME), NDTPA_PAD) || 20061f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_GC_STALETIME, 20072175d87cSNicolas Dichtel NEIGH_VAR(parms, GC_STALETIME), NDTPA_PAD) || 20089a6308d7SDavid S. Miller nla_put_msecs(skb, NDTPA_DELAY_PROBE_TIME, 20092175d87cSNicolas Dichtel NEIGH_VAR(parms, DELAY_PROBE_TIME), NDTPA_PAD) || 20101f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_RETRANS_TIME, 20112175d87cSNicolas Dichtel NEIGH_VAR(parms, RETRANS_TIME), NDTPA_PAD) || 20121f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_ANYCAST_DELAY, 20132175d87cSNicolas Dichtel NEIGH_VAR(parms, ANYCAST_DELAY), NDTPA_PAD) || 20141f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_PROXY_DELAY, 20152175d87cSNicolas Dichtel NEIGH_VAR(parms, PROXY_DELAY), NDTPA_PAD) || 20161f9248e5SJiri Pirko nla_put_msecs(skb, NDTPA_LOCKTIME, 20172175d87cSNicolas Dichtel NEIGH_VAR(parms, LOCKTIME), NDTPA_PAD)) 20189a6308d7SDavid S. Miller goto nla_put_failure; 2019ca860fb3SThomas Graf return nla_nest_end(skb, nest); 2020c7fb64dbSThomas Graf 2021ca860fb3SThomas Graf nla_put_failure: 2022bc3ed28cSThomas Graf nla_nest_cancel(skb, nest); 2023bc3ed28cSThomas Graf return -EMSGSIZE; 2024c7fb64dbSThomas Graf } 2025c7fb64dbSThomas Graf 2026ca860fb3SThomas Graf static int neightbl_fill_info(struct sk_buff *skb, struct neigh_table *tbl, 2027ca860fb3SThomas Graf u32 pid, u32 seq, int type, int flags) 2028c7fb64dbSThomas Graf { 2029c7fb64dbSThomas Graf struct nlmsghdr *nlh; 2030c7fb64dbSThomas Graf struct ndtmsg *ndtmsg; 2031c7fb64dbSThomas Graf 2032ca860fb3SThomas Graf nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndtmsg), flags); 2033ca860fb3SThomas Graf if (nlh == NULL) 203426932566SPatrick McHardy return -EMSGSIZE; 2035c7fb64dbSThomas Graf 2036ca860fb3SThomas Graf ndtmsg = nlmsg_data(nlh); 2037c7fb64dbSThomas Graf 2038c7fb64dbSThomas Graf read_lock_bh(&tbl->lock); 2039c7fb64dbSThomas Graf ndtmsg->ndtm_family = tbl->family; 20409ef1d4c7SPatrick McHardy ndtmsg->ndtm_pad1 = 0; 20419ef1d4c7SPatrick McHardy ndtmsg->ndtm_pad2 = 0; 2042c7fb64dbSThomas Graf 20439a6308d7SDavid S. Miller if (nla_put_string(skb, NDTA_NAME, tbl->id) || 20442175d87cSNicolas Dichtel nla_put_msecs(skb, NDTA_GC_INTERVAL, tbl->gc_interval, NDTA_PAD) || 20459a6308d7SDavid S. Miller nla_put_u32(skb, NDTA_THRESH1, tbl->gc_thresh1) || 20469a6308d7SDavid S. Miller nla_put_u32(skb, NDTA_THRESH2, tbl->gc_thresh2) || 20479a6308d7SDavid S. Miller nla_put_u32(skb, NDTA_THRESH3, tbl->gc_thresh3)) 20489a6308d7SDavid S. Miller goto nla_put_failure; 2049c7fb64dbSThomas Graf { 2050c7fb64dbSThomas Graf unsigned long now = jiffies; 2051c7fb64dbSThomas Graf unsigned int flush_delta = now - tbl->last_flush; 2052c7fb64dbSThomas Graf unsigned int rand_delta = now - tbl->last_rand; 2053d6bf7817SEric Dumazet struct neigh_hash_table *nht; 2054c7fb64dbSThomas Graf struct ndt_config ndc = { 2055c7fb64dbSThomas Graf .ndtc_key_len = tbl->key_len, 2056c7fb64dbSThomas Graf .ndtc_entry_size = tbl->entry_size, 2057c7fb64dbSThomas Graf .ndtc_entries = atomic_read(&tbl->entries), 2058c7fb64dbSThomas Graf .ndtc_last_flush = jiffies_to_msecs(flush_delta), 2059c7fb64dbSThomas Graf .ndtc_last_rand = jiffies_to_msecs(rand_delta), 2060c7fb64dbSThomas Graf .ndtc_proxy_qlen = tbl->proxy_queue.qlen, 2061c7fb64dbSThomas Graf }; 2062c7fb64dbSThomas Graf 2063d6bf7817SEric Dumazet rcu_read_lock_bh(); 2064d6bf7817SEric Dumazet nht = rcu_dereference_bh(tbl->nht); 20652c2aba6cSDavid S. Miller ndc.ndtc_hash_rnd = nht->hash_rnd[0]; 2066cd089336SDavid S. Miller ndc.ndtc_hash_mask = ((1 << nht->hash_shift) - 1); 2067d6bf7817SEric Dumazet rcu_read_unlock_bh(); 2068d6bf7817SEric Dumazet 20699a6308d7SDavid S. Miller if (nla_put(skb, NDTA_CONFIG, sizeof(ndc), &ndc)) 20709a6308d7SDavid S. Miller goto nla_put_failure; 2071c7fb64dbSThomas Graf } 2072c7fb64dbSThomas Graf 2073c7fb64dbSThomas Graf { 2074c7fb64dbSThomas Graf int cpu; 2075c7fb64dbSThomas Graf struct ndt_stats ndst; 2076c7fb64dbSThomas Graf 2077c7fb64dbSThomas Graf memset(&ndst, 0, sizeof(ndst)); 2078c7fb64dbSThomas Graf 20796f912042SKAMEZAWA Hiroyuki for_each_possible_cpu(cpu) { 2080c7fb64dbSThomas Graf struct neigh_statistics *st; 2081c7fb64dbSThomas Graf 2082c7fb64dbSThomas Graf st = per_cpu_ptr(tbl->stats, cpu); 2083c7fb64dbSThomas Graf ndst.ndts_allocs += st->allocs; 2084c7fb64dbSThomas Graf ndst.ndts_destroys += st->destroys; 2085c7fb64dbSThomas Graf ndst.ndts_hash_grows += st->hash_grows; 2086c7fb64dbSThomas Graf ndst.ndts_res_failed += st->res_failed; 2087c7fb64dbSThomas Graf ndst.ndts_lookups += st->lookups; 2088c7fb64dbSThomas Graf ndst.ndts_hits += st->hits; 2089c7fb64dbSThomas Graf ndst.ndts_rcv_probes_mcast += st->rcv_probes_mcast; 2090c7fb64dbSThomas Graf ndst.ndts_rcv_probes_ucast += st->rcv_probes_ucast; 2091c7fb64dbSThomas Graf ndst.ndts_periodic_gc_runs += st->periodic_gc_runs; 2092c7fb64dbSThomas Graf ndst.ndts_forced_gc_runs += st->forced_gc_runs; 2093fb811395SRick Jones ndst.ndts_table_fulls += st->table_fulls; 2094c7fb64dbSThomas Graf } 2095c7fb64dbSThomas Graf 2096b676338fSNicolas Dichtel if (nla_put_64bit(skb, NDTA_STATS, sizeof(ndst), &ndst, 2097b676338fSNicolas Dichtel NDTA_PAD)) 20989a6308d7SDavid S. Miller goto nla_put_failure; 2099c7fb64dbSThomas Graf } 2100c7fb64dbSThomas Graf 2101c7fb64dbSThomas Graf BUG_ON(tbl->parms.dev); 2102c7fb64dbSThomas Graf if (neightbl_fill_parms(skb, &tbl->parms) < 0) 2103ca860fb3SThomas Graf goto nla_put_failure; 2104c7fb64dbSThomas Graf 2105c7fb64dbSThomas Graf read_unlock_bh(&tbl->lock); 2106053c095aSJohannes Berg nlmsg_end(skb, nlh); 2107053c095aSJohannes Berg return 0; 2108c7fb64dbSThomas Graf 2109ca860fb3SThomas Graf nla_put_failure: 2110c7fb64dbSThomas Graf read_unlock_bh(&tbl->lock); 211126932566SPatrick McHardy nlmsg_cancel(skb, nlh); 211226932566SPatrick McHardy return -EMSGSIZE; 2113c7fb64dbSThomas Graf } 2114c7fb64dbSThomas Graf 2115ca860fb3SThomas Graf static int neightbl_fill_param_info(struct sk_buff *skb, 2116ca860fb3SThomas Graf struct neigh_table *tbl, 2117c7fb64dbSThomas Graf struct neigh_parms *parms, 2118ca860fb3SThomas Graf u32 pid, u32 seq, int type, 2119ca860fb3SThomas Graf unsigned int flags) 2120c7fb64dbSThomas Graf { 2121c7fb64dbSThomas Graf struct ndtmsg *ndtmsg; 2122c7fb64dbSThomas Graf struct nlmsghdr *nlh; 2123c7fb64dbSThomas Graf 2124ca860fb3SThomas Graf nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndtmsg), flags); 2125ca860fb3SThomas Graf if (nlh == NULL) 212626932566SPatrick McHardy return -EMSGSIZE; 2127c7fb64dbSThomas Graf 2128ca860fb3SThomas Graf ndtmsg = nlmsg_data(nlh); 2129c7fb64dbSThomas Graf 2130c7fb64dbSThomas Graf read_lock_bh(&tbl->lock); 2131c7fb64dbSThomas Graf ndtmsg->ndtm_family = tbl->family; 21329ef1d4c7SPatrick McHardy ndtmsg->ndtm_pad1 = 0; 21339ef1d4c7SPatrick McHardy ndtmsg->ndtm_pad2 = 0; 2134c7fb64dbSThomas Graf 2135ca860fb3SThomas Graf if (nla_put_string(skb, NDTA_NAME, tbl->id) < 0 || 2136ca860fb3SThomas Graf neightbl_fill_parms(skb, parms) < 0) 2137ca860fb3SThomas Graf goto errout; 2138c7fb64dbSThomas Graf 2139c7fb64dbSThomas Graf read_unlock_bh(&tbl->lock); 2140053c095aSJohannes Berg nlmsg_end(skb, nlh); 2141053c095aSJohannes Berg return 0; 2142ca860fb3SThomas Graf errout: 2143c7fb64dbSThomas Graf read_unlock_bh(&tbl->lock); 214426932566SPatrick McHardy nlmsg_cancel(skb, nlh); 214526932566SPatrick McHardy return -EMSGSIZE; 2146c7fb64dbSThomas Graf } 2147c7fb64dbSThomas Graf 2148ef7c79edSPatrick McHardy static const struct nla_policy nl_neightbl_policy[NDTA_MAX+1] = { 21496b3f8674SThomas Graf [NDTA_NAME] = { .type = NLA_STRING }, 21506b3f8674SThomas Graf [NDTA_THRESH1] = { .type = NLA_U32 }, 21516b3f8674SThomas Graf [NDTA_THRESH2] = { .type = NLA_U32 }, 21526b3f8674SThomas Graf [NDTA_THRESH3] = { .type = NLA_U32 }, 21536b3f8674SThomas Graf [NDTA_GC_INTERVAL] = { .type = NLA_U64 }, 21546b3f8674SThomas Graf [NDTA_PARMS] = { .type = NLA_NESTED }, 21556b3f8674SThomas Graf }; 21566b3f8674SThomas Graf 2157ef7c79edSPatrick McHardy static const struct nla_policy nl_ntbl_parm_policy[NDTPA_MAX+1] = { 21586b3f8674SThomas Graf [NDTPA_IFINDEX] = { .type = NLA_U32 }, 21596b3f8674SThomas Graf [NDTPA_QUEUE_LEN] = { .type = NLA_U32 }, 21606b3f8674SThomas Graf [NDTPA_PROXY_QLEN] = { .type = NLA_U32 }, 21616b3f8674SThomas Graf [NDTPA_APP_PROBES] = { .type = NLA_U32 }, 21626b3f8674SThomas Graf [NDTPA_UCAST_PROBES] = { .type = NLA_U32 }, 21636b3f8674SThomas Graf [NDTPA_MCAST_PROBES] = { .type = NLA_U32 }, 21648da86466SYOSHIFUJI Hideaki/吉藤英明 [NDTPA_MCAST_REPROBES] = { .type = NLA_U32 }, 21656b3f8674SThomas Graf [NDTPA_BASE_REACHABLE_TIME] = { .type = NLA_U64 }, 21666b3f8674SThomas Graf [NDTPA_GC_STALETIME] = { .type = NLA_U64 }, 21676b3f8674SThomas Graf [NDTPA_DELAY_PROBE_TIME] = { .type = NLA_U64 }, 21686b3f8674SThomas Graf [NDTPA_RETRANS_TIME] = { .type = NLA_U64 }, 21696b3f8674SThomas Graf [NDTPA_ANYCAST_DELAY] = { .type = NLA_U64 }, 21706b3f8674SThomas Graf [NDTPA_PROXY_DELAY] = { .type = NLA_U64 }, 21716b3f8674SThomas Graf [NDTPA_LOCKTIME] = { .type = NLA_U64 }, 21726b3f8674SThomas Graf }; 21736b3f8674SThomas Graf 2174c21ef3e3SDavid Ahern static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, 2175c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 2176c7fb64dbSThomas Graf { 21773b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 2178c7fb64dbSThomas Graf struct neigh_table *tbl; 21796b3f8674SThomas Graf struct ndtmsg *ndtmsg; 21806b3f8674SThomas Graf struct nlattr *tb[NDTA_MAX+1]; 2181d7480fd3SWANG Cong bool found = false; 2182d7480fd3SWANG Cong int err, tidx; 2183c7fb64dbSThomas Graf 21848cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(*ndtmsg), tb, NDTA_MAX, 2185c21ef3e3SDavid Ahern nl_neightbl_policy, extack); 21866b3f8674SThomas Graf if (err < 0) 21876b3f8674SThomas Graf goto errout; 2188c7fb64dbSThomas Graf 21896b3f8674SThomas Graf if (tb[NDTA_NAME] == NULL) { 21906b3f8674SThomas Graf err = -EINVAL; 21916b3f8674SThomas Graf goto errout; 21926b3f8674SThomas Graf } 21936b3f8674SThomas Graf 21946b3f8674SThomas Graf ndtmsg = nlmsg_data(nlh); 2195d7480fd3SWANG Cong 2196d7480fd3SWANG Cong for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) { 2197d7480fd3SWANG Cong tbl = neigh_tables[tidx]; 2198d7480fd3SWANG Cong if (!tbl) 2199d7480fd3SWANG Cong continue; 2200c7fb64dbSThomas Graf if (ndtmsg->ndtm_family && tbl->family != ndtmsg->ndtm_family) 2201c7fb64dbSThomas Graf continue; 2202d7480fd3SWANG Cong if (nla_strcmp(tb[NDTA_NAME], tbl->id) == 0) { 2203d7480fd3SWANG Cong found = true; 2204c7fb64dbSThomas Graf break; 2205c7fb64dbSThomas Graf } 2206c7fb64dbSThomas Graf } 2207c7fb64dbSThomas Graf 2208d7480fd3SWANG Cong if (!found) 2209d7480fd3SWANG Cong return -ENOENT; 2210d7480fd3SWANG Cong 2211c7fb64dbSThomas Graf /* 2212c7fb64dbSThomas Graf * We acquire tbl->lock to be nice to the periodic timers and 2213c7fb64dbSThomas Graf * make sure they always see a consistent set of values. 2214c7fb64dbSThomas Graf */ 2215c7fb64dbSThomas Graf write_lock_bh(&tbl->lock); 2216c7fb64dbSThomas Graf 22176b3f8674SThomas Graf if (tb[NDTA_PARMS]) { 22186b3f8674SThomas Graf struct nlattr *tbp[NDTPA_MAX+1]; 2219c7fb64dbSThomas Graf struct neigh_parms *p; 22206b3f8674SThomas Graf int i, ifindex = 0; 2221c7fb64dbSThomas Graf 22228cb08174SJohannes Berg err = nla_parse_nested_deprecated(tbp, NDTPA_MAX, 22238cb08174SJohannes Berg tb[NDTA_PARMS], 2224c21ef3e3SDavid Ahern nl_ntbl_parm_policy, extack); 22256b3f8674SThomas Graf if (err < 0) 22266b3f8674SThomas Graf goto errout_tbl_lock; 2227c7fb64dbSThomas Graf 22286b3f8674SThomas Graf if (tbp[NDTPA_IFINDEX]) 22296b3f8674SThomas Graf ifindex = nla_get_u32(tbp[NDTPA_IFINDEX]); 2230c7fb64dbSThomas Graf 223197fd5bc7STobias Klauser p = lookup_neigh_parms(tbl, net, ifindex); 2232c7fb64dbSThomas Graf if (p == NULL) { 2233c7fb64dbSThomas Graf err = -ENOENT; 22346b3f8674SThomas Graf goto errout_tbl_lock; 2235c7fb64dbSThomas Graf } 2236c7fb64dbSThomas Graf 22376b3f8674SThomas Graf for (i = 1; i <= NDTPA_MAX; i++) { 22386b3f8674SThomas Graf if (tbp[i] == NULL) 22396b3f8674SThomas Graf continue; 2240c7fb64dbSThomas Graf 22416b3f8674SThomas Graf switch (i) { 22426b3f8674SThomas Graf case NDTPA_QUEUE_LEN: 22431f9248e5SJiri Pirko NEIGH_VAR_SET(p, QUEUE_LEN_BYTES, 22441f9248e5SJiri Pirko nla_get_u32(tbp[i]) * 22451f9248e5SJiri Pirko SKB_TRUESIZE(ETH_FRAME_LEN)); 22468b5c171bSEric Dumazet break; 22478b5c171bSEric Dumazet case NDTPA_QUEUE_LENBYTES: 22481f9248e5SJiri Pirko NEIGH_VAR_SET(p, QUEUE_LEN_BYTES, 22491f9248e5SJiri Pirko nla_get_u32(tbp[i])); 22506b3f8674SThomas Graf break; 22516b3f8674SThomas Graf case NDTPA_PROXY_QLEN: 22521f9248e5SJiri Pirko NEIGH_VAR_SET(p, PROXY_QLEN, 22531f9248e5SJiri Pirko nla_get_u32(tbp[i])); 22546b3f8674SThomas Graf break; 22556b3f8674SThomas Graf case NDTPA_APP_PROBES: 22561f9248e5SJiri Pirko NEIGH_VAR_SET(p, APP_PROBES, 22571f9248e5SJiri Pirko nla_get_u32(tbp[i])); 22586b3f8674SThomas Graf break; 22596b3f8674SThomas Graf case NDTPA_UCAST_PROBES: 22601f9248e5SJiri Pirko NEIGH_VAR_SET(p, UCAST_PROBES, 22611f9248e5SJiri Pirko nla_get_u32(tbp[i])); 22626b3f8674SThomas Graf break; 22636b3f8674SThomas Graf case NDTPA_MCAST_PROBES: 22641f9248e5SJiri Pirko NEIGH_VAR_SET(p, MCAST_PROBES, 22651f9248e5SJiri Pirko nla_get_u32(tbp[i])); 22666b3f8674SThomas Graf break; 22678da86466SYOSHIFUJI Hideaki/吉藤英明 case NDTPA_MCAST_REPROBES: 22688da86466SYOSHIFUJI Hideaki/吉藤英明 NEIGH_VAR_SET(p, MCAST_REPROBES, 22698da86466SYOSHIFUJI Hideaki/吉藤英明 nla_get_u32(tbp[i])); 22708da86466SYOSHIFUJI Hideaki/吉藤英明 break; 22716b3f8674SThomas Graf case NDTPA_BASE_REACHABLE_TIME: 22721f9248e5SJiri Pirko NEIGH_VAR_SET(p, BASE_REACHABLE_TIME, 22731f9248e5SJiri Pirko nla_get_msecs(tbp[i])); 22744bf6980dSJean-Francois Remy /* update reachable_time as well, otherwise, the change will 22754bf6980dSJean-Francois Remy * only be effective after the next time neigh_periodic_work 22764bf6980dSJean-Francois Remy * decides to recompute it (can be multiple minutes) 22774bf6980dSJean-Francois Remy */ 22784bf6980dSJean-Francois Remy p->reachable_time = 22794bf6980dSJean-Francois Remy neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); 22806b3f8674SThomas Graf break; 22816b3f8674SThomas Graf case NDTPA_GC_STALETIME: 22821f9248e5SJiri Pirko NEIGH_VAR_SET(p, GC_STALETIME, 22831f9248e5SJiri Pirko nla_get_msecs(tbp[i])); 22846b3f8674SThomas Graf break; 22856b3f8674SThomas Graf case NDTPA_DELAY_PROBE_TIME: 22861f9248e5SJiri Pirko NEIGH_VAR_SET(p, DELAY_PROBE_TIME, 22871f9248e5SJiri Pirko nla_get_msecs(tbp[i])); 22882a4501aeSIdo Schimmel call_netevent_notifiers(NETEVENT_DELAY_PROBE_TIME_UPDATE, p); 22896b3f8674SThomas Graf break; 22906b3f8674SThomas Graf case NDTPA_RETRANS_TIME: 22911f9248e5SJiri Pirko NEIGH_VAR_SET(p, RETRANS_TIME, 22921f9248e5SJiri Pirko nla_get_msecs(tbp[i])); 22936b3f8674SThomas Graf break; 22946b3f8674SThomas Graf case NDTPA_ANYCAST_DELAY: 22953977458cSJiri Pirko NEIGH_VAR_SET(p, ANYCAST_DELAY, 22963977458cSJiri Pirko nla_get_msecs(tbp[i])); 22976b3f8674SThomas Graf break; 22986b3f8674SThomas Graf case NDTPA_PROXY_DELAY: 22993977458cSJiri Pirko NEIGH_VAR_SET(p, PROXY_DELAY, 23003977458cSJiri Pirko nla_get_msecs(tbp[i])); 23016b3f8674SThomas Graf break; 23026b3f8674SThomas Graf case NDTPA_LOCKTIME: 23033977458cSJiri Pirko NEIGH_VAR_SET(p, LOCKTIME, 23043977458cSJiri Pirko nla_get_msecs(tbp[i])); 23056b3f8674SThomas Graf break; 2306c7fb64dbSThomas Graf } 23076b3f8674SThomas Graf } 23086b3f8674SThomas Graf } 23096b3f8674SThomas Graf 2310dc25c676SGao feng err = -ENOENT; 2311dc25c676SGao feng if ((tb[NDTA_THRESH1] || tb[NDTA_THRESH2] || 2312dc25c676SGao feng tb[NDTA_THRESH3] || tb[NDTA_GC_INTERVAL]) && 2313dc25c676SGao feng !net_eq(net, &init_net)) 2314dc25c676SGao feng goto errout_tbl_lock; 2315dc25c676SGao feng 23166b3f8674SThomas Graf if (tb[NDTA_THRESH1]) 23176b3f8674SThomas Graf tbl->gc_thresh1 = nla_get_u32(tb[NDTA_THRESH1]); 23186b3f8674SThomas Graf 23196b3f8674SThomas Graf if (tb[NDTA_THRESH2]) 23206b3f8674SThomas Graf tbl->gc_thresh2 = nla_get_u32(tb[NDTA_THRESH2]); 23216b3f8674SThomas Graf 23226b3f8674SThomas Graf if (tb[NDTA_THRESH3]) 23236b3f8674SThomas Graf tbl->gc_thresh3 = nla_get_u32(tb[NDTA_THRESH3]); 23246b3f8674SThomas Graf 23256b3f8674SThomas Graf if (tb[NDTA_GC_INTERVAL]) 23266b3f8674SThomas Graf tbl->gc_interval = nla_get_msecs(tb[NDTA_GC_INTERVAL]); 2327c7fb64dbSThomas Graf 2328c7fb64dbSThomas Graf err = 0; 2329c7fb64dbSThomas Graf 23306b3f8674SThomas Graf errout_tbl_lock: 2331c7fb64dbSThomas Graf write_unlock_bh(&tbl->lock); 23326b3f8674SThomas Graf errout: 2333c7fb64dbSThomas Graf return err; 2334c7fb64dbSThomas Graf } 2335c7fb64dbSThomas Graf 23369632d47fSDavid Ahern static int neightbl_valid_dump_info(const struct nlmsghdr *nlh, 23379632d47fSDavid Ahern struct netlink_ext_ack *extack) 23389632d47fSDavid Ahern { 23399632d47fSDavid Ahern struct ndtmsg *ndtm; 23409632d47fSDavid Ahern 23419632d47fSDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndtm))) { 23429632d47fSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid header for neighbor table dump request"); 23439632d47fSDavid Ahern return -EINVAL; 23449632d47fSDavid Ahern } 23459632d47fSDavid Ahern 23469632d47fSDavid Ahern ndtm = nlmsg_data(nlh); 23479632d47fSDavid Ahern if (ndtm->ndtm_pad1 || ndtm->ndtm_pad2) { 23489632d47fSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor table dump request"); 23499632d47fSDavid Ahern return -EINVAL; 23509632d47fSDavid Ahern } 23519632d47fSDavid Ahern 23529632d47fSDavid Ahern if (nlmsg_attrlen(nlh, sizeof(*ndtm))) { 23539632d47fSDavid Ahern NL_SET_ERR_MSG(extack, "Invalid data after header in neighbor table dump request"); 23549632d47fSDavid Ahern return -EINVAL; 23559632d47fSDavid Ahern } 23569632d47fSDavid Ahern 23579632d47fSDavid Ahern return 0; 23589632d47fSDavid Ahern } 23599632d47fSDavid Ahern 2360c8822a4eSThomas Graf static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb) 2361c7fb64dbSThomas Graf { 23629632d47fSDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 23633b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 2364ca860fb3SThomas Graf int family, tidx, nidx = 0; 2365ca860fb3SThomas Graf int tbl_skip = cb->args[0]; 2366ca860fb3SThomas Graf int neigh_skip = cb->args[1]; 2367c7fb64dbSThomas Graf struct neigh_table *tbl; 2368c7fb64dbSThomas Graf 23699632d47fSDavid Ahern if (cb->strict_check) { 23709632d47fSDavid Ahern int err = neightbl_valid_dump_info(nlh, cb->extack); 23719632d47fSDavid Ahern 23729632d47fSDavid Ahern if (err < 0) 23739632d47fSDavid Ahern return err; 23749632d47fSDavid Ahern } 23759632d47fSDavid Ahern 23769632d47fSDavid Ahern family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family; 2377c7fb64dbSThomas Graf 2378d7480fd3SWANG Cong for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) { 2379c7fb64dbSThomas Graf struct neigh_parms *p; 2380c7fb64dbSThomas Graf 2381d7480fd3SWANG Cong tbl = neigh_tables[tidx]; 2382d7480fd3SWANG Cong if (!tbl) 2383d7480fd3SWANG Cong continue; 2384d7480fd3SWANG Cong 2385ca860fb3SThomas Graf if (tidx < tbl_skip || (family && tbl->family != family)) 2386c7fb64dbSThomas Graf continue; 2387c7fb64dbSThomas Graf 238815e47304SEric W. Biederman if (neightbl_fill_info(skb, tbl, NETLINK_CB(cb->skb).portid, 23899632d47fSDavid Ahern nlh->nlmsg_seq, RTM_NEWNEIGHTBL, 23907b46a644SDavid S. Miller NLM_F_MULTI) < 0) 2391c7fb64dbSThomas Graf break; 2392c7fb64dbSThomas Graf 239375fbfd33SNicolas Dichtel nidx = 0; 239475fbfd33SNicolas Dichtel p = list_next_entry(&tbl->parms, list); 239575fbfd33SNicolas Dichtel list_for_each_entry_from(p, &tbl->parms_list, list) { 2396878628fbSYOSHIFUJI Hideaki if (!net_eq(neigh_parms_net(p), net)) 2397426b5303SEric W. Biederman continue; 2398426b5303SEric W. Biederman 2399efc683fcSGautam Kachroo if (nidx < neigh_skip) 2400efc683fcSGautam Kachroo goto next; 2401c7fb64dbSThomas Graf 2402ca860fb3SThomas Graf if (neightbl_fill_param_info(skb, tbl, p, 240315e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 24049632d47fSDavid Ahern nlh->nlmsg_seq, 2405ca860fb3SThomas Graf RTM_NEWNEIGHTBL, 24067b46a644SDavid S. Miller NLM_F_MULTI) < 0) 2407c7fb64dbSThomas Graf goto out; 2408efc683fcSGautam Kachroo next: 2409efc683fcSGautam Kachroo nidx++; 2410c7fb64dbSThomas Graf } 2411c7fb64dbSThomas Graf 2412ca860fb3SThomas Graf neigh_skip = 0; 2413c7fb64dbSThomas Graf } 2414c7fb64dbSThomas Graf out: 2415ca860fb3SThomas Graf cb->args[0] = tidx; 2416ca860fb3SThomas Graf cb->args[1] = nidx; 2417c7fb64dbSThomas Graf 2418c7fb64dbSThomas Graf return skb->len; 2419c7fb64dbSThomas Graf } 24201da177e4SLinus Torvalds 24218b8aec50SThomas Graf static int neigh_fill_info(struct sk_buff *skb, struct neighbour *neigh, 24228b8aec50SThomas Graf u32 pid, u32 seq, int type, unsigned int flags) 24231da177e4SLinus Torvalds { 24241da177e4SLinus Torvalds unsigned long now = jiffies; 24251da177e4SLinus Torvalds struct nda_cacheinfo ci; 24268b8aec50SThomas Graf struct nlmsghdr *nlh; 24278b8aec50SThomas Graf struct ndmsg *ndm; 24281da177e4SLinus Torvalds 24298b8aec50SThomas Graf nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags); 24308b8aec50SThomas Graf if (nlh == NULL) 243126932566SPatrick McHardy return -EMSGSIZE; 24328b8aec50SThomas Graf 24338b8aec50SThomas Graf ndm = nlmsg_data(nlh); 24348b8aec50SThomas Graf ndm->ndm_family = neigh->ops->family; 24359ef1d4c7SPatrick McHardy ndm->ndm_pad1 = 0; 24369ef1d4c7SPatrick McHardy ndm->ndm_pad2 = 0; 24378b8aec50SThomas Graf ndm->ndm_flags = neigh->flags; 24388b8aec50SThomas Graf ndm->ndm_type = neigh->type; 24398b8aec50SThomas Graf ndm->ndm_ifindex = neigh->dev->ifindex; 24401da177e4SLinus Torvalds 24419a6308d7SDavid S. Miller if (nla_put(skb, NDA_DST, neigh->tbl->key_len, neigh->primary_key)) 24429a6308d7SDavid S. Miller goto nla_put_failure; 24438b8aec50SThomas Graf 24448b8aec50SThomas Graf read_lock_bh(&neigh->lock); 24458b8aec50SThomas Graf ndm->ndm_state = neigh->nud_state; 24460ed8ddf4SEric Dumazet if (neigh->nud_state & NUD_VALID) { 24470ed8ddf4SEric Dumazet char haddr[MAX_ADDR_LEN]; 24480ed8ddf4SEric Dumazet 24490ed8ddf4SEric Dumazet neigh_ha_snapshot(haddr, neigh, neigh->dev); 24500ed8ddf4SEric Dumazet if (nla_put(skb, NDA_LLADDR, neigh->dev->addr_len, haddr) < 0) { 24518b8aec50SThomas Graf read_unlock_bh(&neigh->lock); 24528b8aec50SThomas Graf goto nla_put_failure; 24538b8aec50SThomas Graf } 24540ed8ddf4SEric Dumazet } 24558b8aec50SThomas Graf 2456b9f5f52cSStephen Hemminger ci.ndm_used = jiffies_to_clock_t(now - neigh->used); 2457b9f5f52cSStephen Hemminger ci.ndm_confirmed = jiffies_to_clock_t(now - neigh->confirmed); 2458b9f5f52cSStephen Hemminger ci.ndm_updated = jiffies_to_clock_t(now - neigh->updated); 24599f237430SReshetova, Elena ci.ndm_refcnt = refcount_read(&neigh->refcnt) - 1; 24608b8aec50SThomas Graf read_unlock_bh(&neigh->lock); 24618b8aec50SThomas Graf 24629a6308d7SDavid S. Miller if (nla_put_u32(skb, NDA_PROBES, atomic_read(&neigh->probes)) || 24639a6308d7SDavid S. Miller nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci)) 24649a6308d7SDavid S. Miller goto nla_put_failure; 24658b8aec50SThomas Graf 2466df9b0e30SDavid Ahern if (neigh->protocol && nla_put_u8(skb, NDA_PROTOCOL, neigh->protocol)) 2467df9b0e30SDavid Ahern goto nla_put_failure; 2468df9b0e30SDavid Ahern 2469053c095aSJohannes Berg nlmsg_end(skb, nlh); 2470053c095aSJohannes Berg return 0; 24718b8aec50SThomas Graf 24728b8aec50SThomas Graf nla_put_failure: 247326932566SPatrick McHardy nlmsg_cancel(skb, nlh); 247426932566SPatrick McHardy return -EMSGSIZE; 24751da177e4SLinus Torvalds } 24761da177e4SLinus Torvalds 247784920c14STony Zelenoff static int pneigh_fill_info(struct sk_buff *skb, struct pneigh_entry *pn, 247884920c14STony Zelenoff u32 pid, u32 seq, int type, unsigned int flags, 247984920c14STony Zelenoff struct neigh_table *tbl) 248084920c14STony Zelenoff { 248184920c14STony Zelenoff struct nlmsghdr *nlh; 248284920c14STony Zelenoff struct ndmsg *ndm; 248384920c14STony Zelenoff 248484920c14STony Zelenoff nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ndm), flags); 248584920c14STony Zelenoff if (nlh == NULL) 248684920c14STony Zelenoff return -EMSGSIZE; 248784920c14STony Zelenoff 248884920c14STony Zelenoff ndm = nlmsg_data(nlh); 248984920c14STony Zelenoff ndm->ndm_family = tbl->family; 249084920c14STony Zelenoff ndm->ndm_pad1 = 0; 249184920c14STony Zelenoff ndm->ndm_pad2 = 0; 249284920c14STony Zelenoff ndm->ndm_flags = pn->flags | NTF_PROXY; 2493545469f7SJun Zhao ndm->ndm_type = RTN_UNICAST; 24946adc5fd6SKonstantin Khlebnikov ndm->ndm_ifindex = pn->dev ? pn->dev->ifindex : 0; 249584920c14STony Zelenoff ndm->ndm_state = NUD_NONE; 249684920c14STony Zelenoff 24979a6308d7SDavid S. Miller if (nla_put(skb, NDA_DST, tbl->key_len, pn->key)) 24989a6308d7SDavid S. Miller goto nla_put_failure; 249984920c14STony Zelenoff 2500df9b0e30SDavid Ahern if (pn->protocol && nla_put_u8(skb, NDA_PROTOCOL, pn->protocol)) 2501df9b0e30SDavid Ahern goto nla_put_failure; 2502df9b0e30SDavid Ahern 2503053c095aSJohannes Berg nlmsg_end(skb, nlh); 2504053c095aSJohannes Berg return 0; 250584920c14STony Zelenoff 250684920c14STony Zelenoff nla_put_failure: 250784920c14STony Zelenoff nlmsg_cancel(skb, nlh); 250884920c14STony Zelenoff return -EMSGSIZE; 250984920c14STony Zelenoff } 251084920c14STony Zelenoff 25117b8f7a40SRoopa Prabhu static void neigh_update_notify(struct neighbour *neigh, u32 nlmsg_pid) 2512d961db35SThomas Graf { 2513d961db35SThomas Graf call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh); 25147b8f7a40SRoopa Prabhu __neigh_notify(neigh, RTM_NEWNEIGH, 0, nlmsg_pid); 2515d961db35SThomas Graf } 25161da177e4SLinus Torvalds 251721fdd092SDavid Ahern static bool neigh_master_filtered(struct net_device *dev, int master_idx) 251821fdd092SDavid Ahern { 251921fdd092SDavid Ahern struct net_device *master; 252021fdd092SDavid Ahern 252121fdd092SDavid Ahern if (!master_idx) 252221fdd092SDavid Ahern return false; 252321fdd092SDavid Ahern 2524aab456dfSEric Dumazet master = dev ? netdev_master_upper_dev_get(dev) : NULL; 252521fdd092SDavid Ahern if (!master || master->ifindex != master_idx) 252621fdd092SDavid Ahern return true; 252721fdd092SDavid Ahern 252821fdd092SDavid Ahern return false; 252921fdd092SDavid Ahern } 253021fdd092SDavid Ahern 253116660f0bSDavid Ahern static bool neigh_ifindex_filtered(struct net_device *dev, int filter_idx) 253216660f0bSDavid Ahern { 2533aab456dfSEric Dumazet if (filter_idx && (!dev || dev->ifindex != filter_idx)) 253416660f0bSDavid Ahern return true; 253516660f0bSDavid Ahern 253616660f0bSDavid Ahern return false; 253716660f0bSDavid Ahern } 253816660f0bSDavid Ahern 25396f52f80eSDavid Ahern struct neigh_dump_filter { 25406f52f80eSDavid Ahern int master_idx; 25416f52f80eSDavid Ahern int dev_idx; 25426f52f80eSDavid Ahern }; 25436f52f80eSDavid Ahern 25441da177e4SLinus Torvalds static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, 25456f52f80eSDavid Ahern struct netlink_callback *cb, 25466f52f80eSDavid Ahern struct neigh_dump_filter *filter) 25471da177e4SLinus Torvalds { 25483b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 25491da177e4SLinus Torvalds struct neighbour *n; 25501da177e4SLinus Torvalds int rc, h, s_h = cb->args[1]; 25511da177e4SLinus Torvalds int idx, s_idx = idx = cb->args[2]; 2552d6bf7817SEric Dumazet struct neigh_hash_table *nht; 255321fdd092SDavid Ahern unsigned int flags = NLM_F_MULTI; 255421fdd092SDavid Ahern 25556f52f80eSDavid Ahern if (filter->dev_idx || filter->master_idx) 255621fdd092SDavid Ahern flags |= NLM_F_DUMP_FILTERED; 25571da177e4SLinus Torvalds 2558d6bf7817SEric Dumazet rcu_read_lock_bh(); 2559d6bf7817SEric Dumazet nht = rcu_dereference_bh(tbl->nht); 2560d6bf7817SEric Dumazet 25614bd6683bSEric Dumazet for (h = s_h; h < (1 << nht->hash_shift); h++) { 25621da177e4SLinus Torvalds if (h > s_h) 25631da177e4SLinus Torvalds s_idx = 0; 2564767e97e1SEric Dumazet for (n = rcu_dereference_bh(nht->hash_buckets[h]), idx = 0; 2565767e97e1SEric Dumazet n != NULL; 2566767e97e1SEric Dumazet n = rcu_dereference_bh(n->next)) { 256718502acdSZhang Shengju if (idx < s_idx || !net_eq(dev_net(n->dev), net)) 256818502acdSZhang Shengju goto next; 25696f52f80eSDavid Ahern if (neigh_ifindex_filtered(n->dev, filter->dev_idx) || 25706f52f80eSDavid Ahern neigh_master_filtered(n->dev, filter->master_idx)) 2571efc683fcSGautam Kachroo goto next; 257215e47304SEric W. Biederman if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid, 25731da177e4SLinus Torvalds cb->nlh->nlmsg_seq, 2574b6544c0bSJamal Hadi Salim RTM_NEWNEIGH, 257521fdd092SDavid Ahern flags) < 0) { 25761da177e4SLinus Torvalds rc = -1; 25771da177e4SLinus Torvalds goto out; 25781da177e4SLinus Torvalds } 2579efc683fcSGautam Kachroo next: 2580efc683fcSGautam Kachroo idx++; 25811da177e4SLinus Torvalds } 25821da177e4SLinus Torvalds } 25831da177e4SLinus Torvalds rc = skb->len; 25841da177e4SLinus Torvalds out: 2585d6bf7817SEric Dumazet rcu_read_unlock_bh(); 25861da177e4SLinus Torvalds cb->args[1] = h; 25871da177e4SLinus Torvalds cb->args[2] = idx; 25881da177e4SLinus Torvalds return rc; 25891da177e4SLinus Torvalds } 25901da177e4SLinus Torvalds 259184920c14STony Zelenoff static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, 25926f52f80eSDavid Ahern struct netlink_callback *cb, 25936f52f80eSDavid Ahern struct neigh_dump_filter *filter) 259484920c14STony Zelenoff { 259584920c14STony Zelenoff struct pneigh_entry *n; 259684920c14STony Zelenoff struct net *net = sock_net(skb->sk); 259784920c14STony Zelenoff int rc, h, s_h = cb->args[3]; 259884920c14STony Zelenoff int idx, s_idx = idx = cb->args[4]; 25996f52f80eSDavid Ahern unsigned int flags = NLM_F_MULTI; 26006f52f80eSDavid Ahern 26016f52f80eSDavid Ahern if (filter->dev_idx || filter->master_idx) 26026f52f80eSDavid Ahern flags |= NLM_F_DUMP_FILTERED; 260384920c14STony Zelenoff 260484920c14STony Zelenoff read_lock_bh(&tbl->lock); 260584920c14STony Zelenoff 26064bd6683bSEric Dumazet for (h = s_h; h <= PNEIGH_HASHMASK; h++) { 260784920c14STony Zelenoff if (h > s_h) 260884920c14STony Zelenoff s_idx = 0; 260984920c14STony Zelenoff for (n = tbl->phash_buckets[h], idx = 0; n; n = n->next) { 261018502acdSZhang Shengju if (idx < s_idx || pneigh_net(n) != net) 261184920c14STony Zelenoff goto next; 26126f52f80eSDavid Ahern if (neigh_ifindex_filtered(n->dev, filter->dev_idx) || 26136f52f80eSDavid Ahern neigh_master_filtered(n->dev, filter->master_idx)) 26146f52f80eSDavid Ahern goto next; 261515e47304SEric W. Biederman if (pneigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid, 261684920c14STony Zelenoff cb->nlh->nlmsg_seq, 26176f52f80eSDavid Ahern RTM_NEWNEIGH, flags, tbl) < 0) { 261884920c14STony Zelenoff read_unlock_bh(&tbl->lock); 261984920c14STony Zelenoff rc = -1; 262084920c14STony Zelenoff goto out; 262184920c14STony Zelenoff } 262284920c14STony Zelenoff next: 262384920c14STony Zelenoff idx++; 262484920c14STony Zelenoff } 262584920c14STony Zelenoff } 262684920c14STony Zelenoff 262784920c14STony Zelenoff read_unlock_bh(&tbl->lock); 262884920c14STony Zelenoff rc = skb->len; 262984920c14STony Zelenoff out: 263084920c14STony Zelenoff cb->args[3] = h; 263184920c14STony Zelenoff cb->args[4] = idx; 263284920c14STony Zelenoff return rc; 263384920c14STony Zelenoff 263484920c14STony Zelenoff } 263584920c14STony Zelenoff 263651183d23SDavid Ahern static int neigh_valid_dump_req(const struct nlmsghdr *nlh, 263751183d23SDavid Ahern bool strict_check, 263851183d23SDavid Ahern struct neigh_dump_filter *filter, 263951183d23SDavid Ahern struct netlink_ext_ack *extack) 264051183d23SDavid Ahern { 264151183d23SDavid Ahern struct nlattr *tb[NDA_MAX + 1]; 264251183d23SDavid Ahern int err, i; 264351183d23SDavid Ahern 264451183d23SDavid Ahern if (strict_check) { 264551183d23SDavid Ahern struct ndmsg *ndm; 264651183d23SDavid Ahern 264751183d23SDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) { 264851183d23SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid header for neighbor dump request"); 264951183d23SDavid Ahern return -EINVAL; 265051183d23SDavid Ahern } 265151183d23SDavid Ahern 265251183d23SDavid Ahern ndm = nlmsg_data(nlh); 265351183d23SDavid Ahern if (ndm->ndm_pad1 || ndm->ndm_pad2 || ndm->ndm_ifindex || 2654c0fde870SDavid Ahern ndm->ndm_state || ndm->ndm_type) { 265551183d23SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor dump request"); 265651183d23SDavid Ahern return -EINVAL; 265751183d23SDavid Ahern } 265851183d23SDavid Ahern 2659c0fde870SDavid Ahern if (ndm->ndm_flags & ~NTF_PROXY) { 2660c0fde870SDavid Ahern NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor dump request"); 2661c0fde870SDavid Ahern return -EINVAL; 2662c0fde870SDavid Ahern } 2663c0fde870SDavid Ahern 26648cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct ndmsg), 26658cb08174SJohannes Berg tb, NDA_MAX, nda_policy, 26668cb08174SJohannes Berg extack); 266751183d23SDavid Ahern } else { 26688cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(struct ndmsg), tb, 26698cb08174SJohannes Berg NDA_MAX, nda_policy, extack); 267051183d23SDavid Ahern } 267151183d23SDavid Ahern if (err < 0) 267251183d23SDavid Ahern return err; 267351183d23SDavid Ahern 267451183d23SDavid Ahern for (i = 0; i <= NDA_MAX; ++i) { 267551183d23SDavid Ahern if (!tb[i]) 267651183d23SDavid Ahern continue; 267751183d23SDavid Ahern 267851183d23SDavid Ahern /* all new attributes should require strict_check */ 267951183d23SDavid Ahern switch (i) { 268051183d23SDavid Ahern case NDA_IFINDEX: 268151183d23SDavid Ahern filter->dev_idx = nla_get_u32(tb[i]); 268251183d23SDavid Ahern break; 268351183d23SDavid Ahern case NDA_MASTER: 268451183d23SDavid Ahern filter->master_idx = nla_get_u32(tb[i]); 268551183d23SDavid Ahern break; 268651183d23SDavid Ahern default: 268751183d23SDavid Ahern if (strict_check) { 268851183d23SDavid Ahern NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor dump request"); 268951183d23SDavid Ahern return -EINVAL; 269051183d23SDavid Ahern } 269151183d23SDavid Ahern } 269251183d23SDavid Ahern } 269351183d23SDavid Ahern 269451183d23SDavid Ahern return 0; 269551183d23SDavid Ahern } 269651183d23SDavid Ahern 2697c8822a4eSThomas Graf static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb) 26981da177e4SLinus Torvalds { 26996f52f80eSDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 27006f52f80eSDavid Ahern struct neigh_dump_filter filter = {}; 27011da177e4SLinus Torvalds struct neigh_table *tbl; 27021da177e4SLinus Torvalds int t, family, s_t; 270384920c14STony Zelenoff int proxy = 0; 27044bd6683bSEric Dumazet int err; 27051da177e4SLinus Torvalds 27066f52f80eSDavid Ahern family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family; 270784920c14STony Zelenoff 270884920c14STony Zelenoff /* check for full ndmsg structure presence, family member is 270984920c14STony Zelenoff * the same for both structures 271084920c14STony Zelenoff */ 27116f52f80eSDavid Ahern if (nlmsg_len(nlh) >= sizeof(struct ndmsg) && 27126f52f80eSDavid Ahern ((struct ndmsg *)nlmsg_data(nlh))->ndm_flags == NTF_PROXY) 271384920c14STony Zelenoff proxy = 1; 271484920c14STony Zelenoff 271551183d23SDavid Ahern err = neigh_valid_dump_req(nlh, cb->strict_check, &filter, cb->extack); 271651183d23SDavid Ahern if (err < 0 && cb->strict_check) 271751183d23SDavid Ahern return err; 271851183d23SDavid Ahern 27191da177e4SLinus Torvalds s_t = cb->args[0]; 27201da177e4SLinus Torvalds 2721d7480fd3SWANG Cong for (t = 0; t < NEIGH_NR_TABLES; t++) { 2722d7480fd3SWANG Cong tbl = neigh_tables[t]; 2723d7480fd3SWANG Cong 2724d7480fd3SWANG Cong if (!tbl) 2725d7480fd3SWANG Cong continue; 27261da177e4SLinus Torvalds if (t < s_t || (family && tbl->family != family)) 27271da177e4SLinus Torvalds continue; 27281da177e4SLinus Torvalds if (t > s_t) 27291da177e4SLinus Torvalds memset(&cb->args[1], 0, sizeof(cb->args) - 27301da177e4SLinus Torvalds sizeof(cb->args[0])); 273184920c14STony Zelenoff if (proxy) 27326f52f80eSDavid Ahern err = pneigh_dump_table(tbl, skb, cb, &filter); 273384920c14STony Zelenoff else 27346f52f80eSDavid Ahern err = neigh_dump_table(tbl, skb, cb, &filter); 27354bd6683bSEric Dumazet if (err < 0) 27364bd6683bSEric Dumazet break; 27371da177e4SLinus Torvalds } 27381da177e4SLinus Torvalds 27391da177e4SLinus Torvalds cb->args[0] = t; 27401da177e4SLinus Torvalds return skb->len; 27411da177e4SLinus Torvalds } 27421da177e4SLinus Torvalds 274382cbb5c6SRoopa Prabhu static int neigh_valid_get_req(const struct nlmsghdr *nlh, 274482cbb5c6SRoopa Prabhu struct neigh_table **tbl, 274582cbb5c6SRoopa Prabhu void **dst, int *dev_idx, u8 *ndm_flags, 274682cbb5c6SRoopa Prabhu struct netlink_ext_ack *extack) 274782cbb5c6SRoopa Prabhu { 274882cbb5c6SRoopa Prabhu struct nlattr *tb[NDA_MAX + 1]; 274982cbb5c6SRoopa Prabhu struct ndmsg *ndm; 275082cbb5c6SRoopa Prabhu int err, i; 275182cbb5c6SRoopa Prabhu 275282cbb5c6SRoopa Prabhu if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) { 275382cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Invalid header for neighbor get request"); 275482cbb5c6SRoopa Prabhu return -EINVAL; 275582cbb5c6SRoopa Prabhu } 275682cbb5c6SRoopa Prabhu 275782cbb5c6SRoopa Prabhu ndm = nlmsg_data(nlh); 275882cbb5c6SRoopa Prabhu if (ndm->ndm_pad1 || ndm->ndm_pad2 || ndm->ndm_state || 275982cbb5c6SRoopa Prabhu ndm->ndm_type) { 276082cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor get request"); 276182cbb5c6SRoopa Prabhu return -EINVAL; 276282cbb5c6SRoopa Prabhu } 276382cbb5c6SRoopa Prabhu 276482cbb5c6SRoopa Prabhu if (ndm->ndm_flags & ~NTF_PROXY) { 276582cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor get request"); 276682cbb5c6SRoopa Prabhu return -EINVAL; 276782cbb5c6SRoopa Prabhu } 276882cbb5c6SRoopa Prabhu 27698cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct ndmsg), tb, 27708cb08174SJohannes Berg NDA_MAX, nda_policy, extack); 277182cbb5c6SRoopa Prabhu if (err < 0) 277282cbb5c6SRoopa Prabhu return err; 277382cbb5c6SRoopa Prabhu 277482cbb5c6SRoopa Prabhu *ndm_flags = ndm->ndm_flags; 277582cbb5c6SRoopa Prabhu *dev_idx = ndm->ndm_ifindex; 277682cbb5c6SRoopa Prabhu *tbl = neigh_find_table(ndm->ndm_family); 277782cbb5c6SRoopa Prabhu if (*tbl == NULL) { 277882cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Unsupported family in header for neighbor get request"); 277982cbb5c6SRoopa Prabhu return -EAFNOSUPPORT; 278082cbb5c6SRoopa Prabhu } 278182cbb5c6SRoopa Prabhu 278282cbb5c6SRoopa Prabhu for (i = 0; i <= NDA_MAX; ++i) { 278382cbb5c6SRoopa Prabhu if (!tb[i]) 278482cbb5c6SRoopa Prabhu continue; 278582cbb5c6SRoopa Prabhu 278682cbb5c6SRoopa Prabhu switch (i) { 278782cbb5c6SRoopa Prabhu case NDA_DST: 278882cbb5c6SRoopa Prabhu if (nla_len(tb[i]) != (int)(*tbl)->key_len) { 278982cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Invalid network address in neighbor get request"); 279082cbb5c6SRoopa Prabhu return -EINVAL; 279182cbb5c6SRoopa Prabhu } 279282cbb5c6SRoopa Prabhu *dst = nla_data(tb[i]); 279382cbb5c6SRoopa Prabhu break; 279482cbb5c6SRoopa Prabhu default: 279582cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor get request"); 279682cbb5c6SRoopa Prabhu return -EINVAL; 279782cbb5c6SRoopa Prabhu } 279882cbb5c6SRoopa Prabhu } 279982cbb5c6SRoopa Prabhu 280082cbb5c6SRoopa Prabhu return 0; 280182cbb5c6SRoopa Prabhu } 280282cbb5c6SRoopa Prabhu 280382cbb5c6SRoopa Prabhu static inline size_t neigh_nlmsg_size(void) 280482cbb5c6SRoopa Prabhu { 280582cbb5c6SRoopa Prabhu return NLMSG_ALIGN(sizeof(struct ndmsg)) 280682cbb5c6SRoopa Prabhu + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */ 280782cbb5c6SRoopa Prabhu + nla_total_size(MAX_ADDR_LEN) /* NDA_LLADDR */ 280882cbb5c6SRoopa Prabhu + nla_total_size(sizeof(struct nda_cacheinfo)) 280982cbb5c6SRoopa Prabhu + nla_total_size(4) /* NDA_PROBES */ 281082cbb5c6SRoopa Prabhu + nla_total_size(1); /* NDA_PROTOCOL */ 281182cbb5c6SRoopa Prabhu } 281282cbb5c6SRoopa Prabhu 281382cbb5c6SRoopa Prabhu static int neigh_get_reply(struct net *net, struct neighbour *neigh, 281482cbb5c6SRoopa Prabhu u32 pid, u32 seq) 281582cbb5c6SRoopa Prabhu { 281682cbb5c6SRoopa Prabhu struct sk_buff *skb; 281782cbb5c6SRoopa Prabhu int err = 0; 281882cbb5c6SRoopa Prabhu 281982cbb5c6SRoopa Prabhu skb = nlmsg_new(neigh_nlmsg_size(), GFP_KERNEL); 282082cbb5c6SRoopa Prabhu if (!skb) 282182cbb5c6SRoopa Prabhu return -ENOBUFS; 282282cbb5c6SRoopa Prabhu 282382cbb5c6SRoopa Prabhu err = neigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0); 282482cbb5c6SRoopa Prabhu if (err) { 282582cbb5c6SRoopa Prabhu kfree_skb(skb); 282682cbb5c6SRoopa Prabhu goto errout; 282782cbb5c6SRoopa Prabhu } 282882cbb5c6SRoopa Prabhu 282982cbb5c6SRoopa Prabhu err = rtnl_unicast(skb, net, pid); 283082cbb5c6SRoopa Prabhu errout: 283182cbb5c6SRoopa Prabhu return err; 283282cbb5c6SRoopa Prabhu } 283382cbb5c6SRoopa Prabhu 283482cbb5c6SRoopa Prabhu static inline size_t pneigh_nlmsg_size(void) 283582cbb5c6SRoopa Prabhu { 283682cbb5c6SRoopa Prabhu return NLMSG_ALIGN(sizeof(struct ndmsg)) 2837463561e6SColin Ian King + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */ 283882cbb5c6SRoopa Prabhu + nla_total_size(1); /* NDA_PROTOCOL */ 283982cbb5c6SRoopa Prabhu } 284082cbb5c6SRoopa Prabhu 284182cbb5c6SRoopa Prabhu static int pneigh_get_reply(struct net *net, struct pneigh_entry *neigh, 284282cbb5c6SRoopa Prabhu u32 pid, u32 seq, struct neigh_table *tbl) 284382cbb5c6SRoopa Prabhu { 284482cbb5c6SRoopa Prabhu struct sk_buff *skb; 284582cbb5c6SRoopa Prabhu int err = 0; 284682cbb5c6SRoopa Prabhu 284782cbb5c6SRoopa Prabhu skb = nlmsg_new(pneigh_nlmsg_size(), GFP_KERNEL); 284882cbb5c6SRoopa Prabhu if (!skb) 284982cbb5c6SRoopa Prabhu return -ENOBUFS; 285082cbb5c6SRoopa Prabhu 285182cbb5c6SRoopa Prabhu err = pneigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0, tbl); 285282cbb5c6SRoopa Prabhu if (err) { 285382cbb5c6SRoopa Prabhu kfree_skb(skb); 285482cbb5c6SRoopa Prabhu goto errout; 285582cbb5c6SRoopa Prabhu } 285682cbb5c6SRoopa Prabhu 285782cbb5c6SRoopa Prabhu err = rtnl_unicast(skb, net, pid); 285882cbb5c6SRoopa Prabhu errout: 285982cbb5c6SRoopa Prabhu return err; 286082cbb5c6SRoopa Prabhu } 286182cbb5c6SRoopa Prabhu 286282cbb5c6SRoopa Prabhu static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh, 286382cbb5c6SRoopa Prabhu struct netlink_ext_ack *extack) 286482cbb5c6SRoopa Prabhu { 286582cbb5c6SRoopa Prabhu struct net *net = sock_net(in_skb->sk); 286682cbb5c6SRoopa Prabhu struct net_device *dev = NULL; 286782cbb5c6SRoopa Prabhu struct neigh_table *tbl = NULL; 286882cbb5c6SRoopa Prabhu struct neighbour *neigh; 286982cbb5c6SRoopa Prabhu void *dst = NULL; 287082cbb5c6SRoopa Prabhu u8 ndm_flags = 0; 287182cbb5c6SRoopa Prabhu int dev_idx = 0; 287282cbb5c6SRoopa Prabhu int err; 287382cbb5c6SRoopa Prabhu 287482cbb5c6SRoopa Prabhu err = neigh_valid_get_req(nlh, &tbl, &dst, &dev_idx, &ndm_flags, 287582cbb5c6SRoopa Prabhu extack); 287682cbb5c6SRoopa Prabhu if (err < 0) 287782cbb5c6SRoopa Prabhu return err; 287882cbb5c6SRoopa Prabhu 287982cbb5c6SRoopa Prabhu if (dev_idx) { 288082cbb5c6SRoopa Prabhu dev = __dev_get_by_index(net, dev_idx); 288182cbb5c6SRoopa Prabhu if (!dev) { 288282cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Unknown device ifindex"); 288382cbb5c6SRoopa Prabhu return -ENODEV; 288482cbb5c6SRoopa Prabhu } 288582cbb5c6SRoopa Prabhu } 288682cbb5c6SRoopa Prabhu 288782cbb5c6SRoopa Prabhu if (!dst) { 288882cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Network address not specified"); 288982cbb5c6SRoopa Prabhu return -EINVAL; 289082cbb5c6SRoopa Prabhu } 289182cbb5c6SRoopa Prabhu 289282cbb5c6SRoopa Prabhu if (ndm_flags & NTF_PROXY) { 289382cbb5c6SRoopa Prabhu struct pneigh_entry *pn; 289482cbb5c6SRoopa Prabhu 289582cbb5c6SRoopa Prabhu pn = pneigh_lookup(tbl, net, dst, dev, 0); 289682cbb5c6SRoopa Prabhu if (!pn) { 289782cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Proxy neighbour entry not found"); 289882cbb5c6SRoopa Prabhu return -ENOENT; 289982cbb5c6SRoopa Prabhu } 290082cbb5c6SRoopa Prabhu return pneigh_get_reply(net, pn, NETLINK_CB(in_skb).portid, 290182cbb5c6SRoopa Prabhu nlh->nlmsg_seq, tbl); 290282cbb5c6SRoopa Prabhu } 290382cbb5c6SRoopa Prabhu 290482cbb5c6SRoopa Prabhu if (!dev) { 290582cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "No device specified"); 290682cbb5c6SRoopa Prabhu return -EINVAL; 290782cbb5c6SRoopa Prabhu } 290882cbb5c6SRoopa Prabhu 290982cbb5c6SRoopa Prabhu neigh = neigh_lookup(tbl, dst, dev); 291082cbb5c6SRoopa Prabhu if (!neigh) { 291182cbb5c6SRoopa Prabhu NL_SET_ERR_MSG(extack, "Neighbour entry not found"); 291282cbb5c6SRoopa Prabhu return -ENOENT; 291382cbb5c6SRoopa Prabhu } 291482cbb5c6SRoopa Prabhu 291582cbb5c6SRoopa Prabhu err = neigh_get_reply(net, neigh, NETLINK_CB(in_skb).portid, 291682cbb5c6SRoopa Prabhu nlh->nlmsg_seq); 291782cbb5c6SRoopa Prabhu 291882cbb5c6SRoopa Prabhu neigh_release(neigh); 291982cbb5c6SRoopa Prabhu 292082cbb5c6SRoopa Prabhu return err; 292182cbb5c6SRoopa Prabhu } 292282cbb5c6SRoopa Prabhu 29231da177e4SLinus Torvalds void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie) 29241da177e4SLinus Torvalds { 29251da177e4SLinus Torvalds int chain; 2926d6bf7817SEric Dumazet struct neigh_hash_table *nht; 29271da177e4SLinus Torvalds 2928d6bf7817SEric Dumazet rcu_read_lock_bh(); 2929d6bf7817SEric Dumazet nht = rcu_dereference_bh(tbl->nht); 2930d6bf7817SEric Dumazet 2931767e97e1SEric Dumazet read_lock(&tbl->lock); /* avoid resizes */ 2932cd089336SDavid S. Miller for (chain = 0; chain < (1 << nht->hash_shift); chain++) { 29331da177e4SLinus Torvalds struct neighbour *n; 29341da177e4SLinus Torvalds 2935767e97e1SEric Dumazet for (n = rcu_dereference_bh(nht->hash_buckets[chain]); 2936767e97e1SEric Dumazet n != NULL; 2937767e97e1SEric Dumazet n = rcu_dereference_bh(n->next)) 29381da177e4SLinus Torvalds cb(n, cookie); 29391da177e4SLinus Torvalds } 2940d6bf7817SEric Dumazet read_unlock(&tbl->lock); 2941d6bf7817SEric Dumazet rcu_read_unlock_bh(); 29421da177e4SLinus Torvalds } 29431da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_for_each); 29441da177e4SLinus Torvalds 29451da177e4SLinus Torvalds /* The tbl->lock must be held as a writer and BH disabled. */ 29461da177e4SLinus Torvalds void __neigh_for_each_release(struct neigh_table *tbl, 29471da177e4SLinus Torvalds int (*cb)(struct neighbour *)) 29481da177e4SLinus Torvalds { 29491da177e4SLinus Torvalds int chain; 2950d6bf7817SEric Dumazet struct neigh_hash_table *nht; 29511da177e4SLinus Torvalds 2952d6bf7817SEric Dumazet nht = rcu_dereference_protected(tbl->nht, 2953d6bf7817SEric Dumazet lockdep_is_held(&tbl->lock)); 2954cd089336SDavid S. Miller for (chain = 0; chain < (1 << nht->hash_shift); chain++) { 2955767e97e1SEric Dumazet struct neighbour *n; 2956767e97e1SEric Dumazet struct neighbour __rcu **np; 29571da177e4SLinus Torvalds 2958d6bf7817SEric Dumazet np = &nht->hash_buckets[chain]; 2959767e97e1SEric Dumazet while ((n = rcu_dereference_protected(*np, 2960767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))) != NULL) { 29611da177e4SLinus Torvalds int release; 29621da177e4SLinus Torvalds 29631da177e4SLinus Torvalds write_lock(&n->lock); 29641da177e4SLinus Torvalds release = cb(n); 29651da177e4SLinus Torvalds if (release) { 2966767e97e1SEric Dumazet rcu_assign_pointer(*np, 2967767e97e1SEric Dumazet rcu_dereference_protected(n->next, 2968767e97e1SEric Dumazet lockdep_is_held(&tbl->lock))); 296958956317SDavid Ahern neigh_mark_dead(n); 29701da177e4SLinus Torvalds } else 29711da177e4SLinus Torvalds np = &n->next; 29721da177e4SLinus Torvalds write_unlock(&n->lock); 29734f494554SThomas Graf if (release) 29744f494554SThomas Graf neigh_cleanup_and_release(n); 29751da177e4SLinus Torvalds } 29761da177e4SLinus Torvalds } 2977ecbb4169SAlexey Kuznetsov } 29781da177e4SLinus Torvalds EXPORT_SYMBOL(__neigh_for_each_release); 29791da177e4SLinus Torvalds 2980b79bda3dSEric W. Biederman int neigh_xmit(int index, struct net_device *dev, 29814fd3d7d9SEric W. Biederman const void *addr, struct sk_buff *skb) 29824fd3d7d9SEric W. Biederman { 2983b79bda3dSEric W. Biederman int err = -EAFNOSUPPORT; 2984b79bda3dSEric W. Biederman if (likely(index < NEIGH_NR_TABLES)) { 29854fd3d7d9SEric W. Biederman struct neigh_table *tbl; 29864fd3d7d9SEric W. Biederman struct neighbour *neigh; 29874fd3d7d9SEric W. Biederman 2988b79bda3dSEric W. Biederman tbl = neigh_tables[index]; 29894fd3d7d9SEric W. Biederman if (!tbl) 29904fd3d7d9SEric W. Biederman goto out; 2991b560f03dSDavid Barroso rcu_read_lock_bh(); 29924b2a2bfeSDavid Ahern if (index == NEIGH_ARP_TABLE) { 29934b2a2bfeSDavid Ahern u32 key = *((u32 *)addr); 29944b2a2bfeSDavid Ahern 29954b2a2bfeSDavid Ahern neigh = __ipv4_neigh_lookup_noref(dev, key); 29964b2a2bfeSDavid Ahern } else { 29974fd3d7d9SEric W. Biederman neigh = __neigh_lookup_noref(tbl, addr, dev); 29984b2a2bfeSDavid Ahern } 29994fd3d7d9SEric W. Biederman if (!neigh) 30004fd3d7d9SEric W. Biederman neigh = __neigh_create(tbl, addr, dev, false); 30014fd3d7d9SEric W. Biederman err = PTR_ERR(neigh); 3002b560f03dSDavid Barroso if (IS_ERR(neigh)) { 3003b560f03dSDavid Barroso rcu_read_unlock_bh(); 30044fd3d7d9SEric W. Biederman goto out_kfree_skb; 3005b560f03dSDavid Barroso } 30064fd3d7d9SEric W. Biederman err = neigh->output(neigh, skb); 3007b560f03dSDavid Barroso rcu_read_unlock_bh(); 30084fd3d7d9SEric W. Biederman } 3009b79bda3dSEric W. Biederman else if (index == NEIGH_LINK_TABLE) { 3010b79bda3dSEric W. Biederman err = dev_hard_header(skb, dev, ntohs(skb->protocol), 3011b79bda3dSEric W. Biederman addr, NULL, skb->len); 3012b79bda3dSEric W. Biederman if (err < 0) 3013b79bda3dSEric W. Biederman goto out_kfree_skb; 3014b79bda3dSEric W. Biederman err = dev_queue_xmit(skb); 3015b79bda3dSEric W. Biederman } 30164fd3d7d9SEric W. Biederman out: 30174fd3d7d9SEric W. Biederman return err; 30184fd3d7d9SEric W. Biederman out_kfree_skb: 30194fd3d7d9SEric W. Biederman kfree_skb(skb); 30204fd3d7d9SEric W. Biederman goto out; 30214fd3d7d9SEric W. Biederman } 30224fd3d7d9SEric W. Biederman EXPORT_SYMBOL(neigh_xmit); 30234fd3d7d9SEric W. Biederman 30241da177e4SLinus Torvalds #ifdef CONFIG_PROC_FS 30251da177e4SLinus Torvalds 30261da177e4SLinus Torvalds static struct neighbour *neigh_get_first(struct seq_file *seq) 30271da177e4SLinus Torvalds { 30281da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 30291218854aSYOSHIFUJI Hideaki struct net *net = seq_file_net(seq); 3030d6bf7817SEric Dumazet struct neigh_hash_table *nht = state->nht; 30311da177e4SLinus Torvalds struct neighbour *n = NULL; 30321da177e4SLinus Torvalds int bucket = state->bucket; 30331da177e4SLinus Torvalds 30341da177e4SLinus Torvalds state->flags &= ~NEIGH_SEQ_IS_PNEIGH; 3035cd089336SDavid S. Miller for (bucket = 0; bucket < (1 << nht->hash_shift); bucket++) { 3036767e97e1SEric Dumazet n = rcu_dereference_bh(nht->hash_buckets[bucket]); 30371da177e4SLinus Torvalds 30381da177e4SLinus Torvalds while (n) { 3039878628fbSYOSHIFUJI Hideaki if (!net_eq(dev_net(n->dev), net)) 3040426b5303SEric W. Biederman goto next; 30411da177e4SLinus Torvalds if (state->neigh_sub_iter) { 30421da177e4SLinus Torvalds loff_t fakep = 0; 30431da177e4SLinus Torvalds void *v; 30441da177e4SLinus Torvalds 30451da177e4SLinus Torvalds v = state->neigh_sub_iter(state, n, &fakep); 30461da177e4SLinus Torvalds if (!v) 30471da177e4SLinus Torvalds goto next; 30481da177e4SLinus Torvalds } 30491da177e4SLinus Torvalds if (!(state->flags & NEIGH_SEQ_SKIP_NOARP)) 30501da177e4SLinus Torvalds break; 30511da177e4SLinus Torvalds if (n->nud_state & ~NUD_NOARP) 30521da177e4SLinus Torvalds break; 30531da177e4SLinus Torvalds next: 3054767e97e1SEric Dumazet n = rcu_dereference_bh(n->next); 30551da177e4SLinus Torvalds } 30561da177e4SLinus Torvalds 30571da177e4SLinus Torvalds if (n) 30581da177e4SLinus Torvalds break; 30591da177e4SLinus Torvalds } 30601da177e4SLinus Torvalds state->bucket = bucket; 30611da177e4SLinus Torvalds 30621da177e4SLinus Torvalds return n; 30631da177e4SLinus Torvalds } 30641da177e4SLinus Torvalds 30651da177e4SLinus Torvalds static struct neighbour *neigh_get_next(struct seq_file *seq, 30661da177e4SLinus Torvalds struct neighbour *n, 30671da177e4SLinus Torvalds loff_t *pos) 30681da177e4SLinus Torvalds { 30691da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 30701218854aSYOSHIFUJI Hideaki struct net *net = seq_file_net(seq); 3071d6bf7817SEric Dumazet struct neigh_hash_table *nht = state->nht; 30721da177e4SLinus Torvalds 30731da177e4SLinus Torvalds if (state->neigh_sub_iter) { 30741da177e4SLinus Torvalds void *v = state->neigh_sub_iter(state, n, pos); 30751da177e4SLinus Torvalds if (v) 30761da177e4SLinus Torvalds return n; 30771da177e4SLinus Torvalds } 3078767e97e1SEric Dumazet n = rcu_dereference_bh(n->next); 30791da177e4SLinus Torvalds 30801da177e4SLinus Torvalds while (1) { 30811da177e4SLinus Torvalds while (n) { 3082878628fbSYOSHIFUJI Hideaki if (!net_eq(dev_net(n->dev), net)) 3083426b5303SEric W. Biederman goto next; 30841da177e4SLinus Torvalds if (state->neigh_sub_iter) { 30851da177e4SLinus Torvalds void *v = state->neigh_sub_iter(state, n, pos); 30861da177e4SLinus Torvalds if (v) 30871da177e4SLinus Torvalds return n; 30881da177e4SLinus Torvalds goto next; 30891da177e4SLinus Torvalds } 30901da177e4SLinus Torvalds if (!(state->flags & NEIGH_SEQ_SKIP_NOARP)) 30911da177e4SLinus Torvalds break; 30921da177e4SLinus Torvalds 30931da177e4SLinus Torvalds if (n->nud_state & ~NUD_NOARP) 30941da177e4SLinus Torvalds break; 30951da177e4SLinus Torvalds next: 3096767e97e1SEric Dumazet n = rcu_dereference_bh(n->next); 30971da177e4SLinus Torvalds } 30981da177e4SLinus Torvalds 30991da177e4SLinus Torvalds if (n) 31001da177e4SLinus Torvalds break; 31011da177e4SLinus Torvalds 3102cd089336SDavid S. Miller if (++state->bucket >= (1 << nht->hash_shift)) 31031da177e4SLinus Torvalds break; 31041da177e4SLinus Torvalds 3105767e97e1SEric Dumazet n = rcu_dereference_bh(nht->hash_buckets[state->bucket]); 31061da177e4SLinus Torvalds } 31071da177e4SLinus Torvalds 31081da177e4SLinus Torvalds if (n && pos) 31091da177e4SLinus Torvalds --(*pos); 31101da177e4SLinus Torvalds return n; 31111da177e4SLinus Torvalds } 31121da177e4SLinus Torvalds 31131da177e4SLinus Torvalds static struct neighbour *neigh_get_idx(struct seq_file *seq, loff_t *pos) 31141da177e4SLinus Torvalds { 31151da177e4SLinus Torvalds struct neighbour *n = neigh_get_first(seq); 31161da177e4SLinus Torvalds 31171da177e4SLinus Torvalds if (n) { 3118745e2031SChris Larson --(*pos); 31191da177e4SLinus Torvalds while (*pos) { 31201da177e4SLinus Torvalds n = neigh_get_next(seq, n, pos); 31211da177e4SLinus Torvalds if (!n) 31221da177e4SLinus Torvalds break; 31231da177e4SLinus Torvalds } 31241da177e4SLinus Torvalds } 31251da177e4SLinus Torvalds return *pos ? NULL : n; 31261da177e4SLinus Torvalds } 31271da177e4SLinus Torvalds 31281da177e4SLinus Torvalds static struct pneigh_entry *pneigh_get_first(struct seq_file *seq) 31291da177e4SLinus Torvalds { 31301da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 31311218854aSYOSHIFUJI Hideaki struct net *net = seq_file_net(seq); 31321da177e4SLinus Torvalds struct neigh_table *tbl = state->tbl; 31331da177e4SLinus Torvalds struct pneigh_entry *pn = NULL; 31341da177e4SLinus Torvalds int bucket = state->bucket; 31351da177e4SLinus Torvalds 31361da177e4SLinus Torvalds state->flags |= NEIGH_SEQ_IS_PNEIGH; 31371da177e4SLinus Torvalds for (bucket = 0; bucket <= PNEIGH_HASHMASK; bucket++) { 31381da177e4SLinus Torvalds pn = tbl->phash_buckets[bucket]; 3139878628fbSYOSHIFUJI Hideaki while (pn && !net_eq(pneigh_net(pn), net)) 3140426b5303SEric W. Biederman pn = pn->next; 31411da177e4SLinus Torvalds if (pn) 31421da177e4SLinus Torvalds break; 31431da177e4SLinus Torvalds } 31441da177e4SLinus Torvalds state->bucket = bucket; 31451da177e4SLinus Torvalds 31461da177e4SLinus Torvalds return pn; 31471da177e4SLinus Torvalds } 31481da177e4SLinus Torvalds 31491da177e4SLinus Torvalds static struct pneigh_entry *pneigh_get_next(struct seq_file *seq, 31501da177e4SLinus Torvalds struct pneigh_entry *pn, 31511da177e4SLinus Torvalds loff_t *pos) 31521da177e4SLinus Torvalds { 31531da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 31541218854aSYOSHIFUJI Hideaki struct net *net = seq_file_net(seq); 31551da177e4SLinus Torvalds struct neigh_table *tbl = state->tbl; 31561da177e4SLinus Torvalds 3157df07a94cSJorge Boncompte [DTI2] do { 31581da177e4SLinus Torvalds pn = pn->next; 3159df07a94cSJorge Boncompte [DTI2] } while (pn && !net_eq(pneigh_net(pn), net)); 3160df07a94cSJorge Boncompte [DTI2] 31611da177e4SLinus Torvalds while (!pn) { 31621da177e4SLinus Torvalds if (++state->bucket > PNEIGH_HASHMASK) 31631da177e4SLinus Torvalds break; 31641da177e4SLinus Torvalds pn = tbl->phash_buckets[state->bucket]; 3165878628fbSYOSHIFUJI Hideaki while (pn && !net_eq(pneigh_net(pn), net)) 3166426b5303SEric W. Biederman pn = pn->next; 31671da177e4SLinus Torvalds if (pn) 31681da177e4SLinus Torvalds break; 31691da177e4SLinus Torvalds } 31701da177e4SLinus Torvalds 31711da177e4SLinus Torvalds if (pn && pos) 31721da177e4SLinus Torvalds --(*pos); 31731da177e4SLinus Torvalds 31741da177e4SLinus Torvalds return pn; 31751da177e4SLinus Torvalds } 31761da177e4SLinus Torvalds 31771da177e4SLinus Torvalds static struct pneigh_entry *pneigh_get_idx(struct seq_file *seq, loff_t *pos) 31781da177e4SLinus Torvalds { 31791da177e4SLinus Torvalds struct pneigh_entry *pn = pneigh_get_first(seq); 31801da177e4SLinus Torvalds 31811da177e4SLinus Torvalds if (pn) { 3182745e2031SChris Larson --(*pos); 31831da177e4SLinus Torvalds while (*pos) { 31841da177e4SLinus Torvalds pn = pneigh_get_next(seq, pn, pos); 31851da177e4SLinus Torvalds if (!pn) 31861da177e4SLinus Torvalds break; 31871da177e4SLinus Torvalds } 31881da177e4SLinus Torvalds } 31891da177e4SLinus Torvalds return *pos ? NULL : pn; 31901da177e4SLinus Torvalds } 31911da177e4SLinus Torvalds 31921da177e4SLinus Torvalds static void *neigh_get_idx_any(struct seq_file *seq, loff_t *pos) 31931da177e4SLinus Torvalds { 31941da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 31951da177e4SLinus Torvalds void *rc; 3196745e2031SChris Larson loff_t idxpos = *pos; 31971da177e4SLinus Torvalds 3198745e2031SChris Larson rc = neigh_get_idx(seq, &idxpos); 31991da177e4SLinus Torvalds if (!rc && !(state->flags & NEIGH_SEQ_NEIGH_ONLY)) 3200745e2031SChris Larson rc = pneigh_get_idx(seq, &idxpos); 32011da177e4SLinus Torvalds 32021da177e4SLinus Torvalds return rc; 32031da177e4SLinus Torvalds } 32041da177e4SLinus Torvalds 32051da177e4SLinus Torvalds void *neigh_seq_start(struct seq_file *seq, loff_t *pos, struct neigh_table *tbl, unsigned int neigh_seq_flags) 3206d6bf7817SEric Dumazet __acquires(rcu_bh) 32071da177e4SLinus Torvalds { 32081da177e4SLinus Torvalds struct neigh_seq_state *state = seq->private; 32091da177e4SLinus Torvalds 32101da177e4SLinus Torvalds state->tbl = tbl; 32111da177e4SLinus Torvalds state->bucket = 0; 32121da177e4SLinus Torvalds state->flags = (neigh_seq_flags & ~NEIGH_SEQ_IS_PNEIGH); 32131da177e4SLinus Torvalds 3214d6bf7817SEric Dumazet rcu_read_lock_bh(); 3215d6bf7817SEric Dumazet state->nht = rcu_dereference_bh(tbl->nht); 3216767e97e1SEric Dumazet 3217745e2031SChris Larson return *pos ? neigh_get_idx_any(seq, pos) : SEQ_START_TOKEN; 32181da177e4SLinus Torvalds } 32191da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_seq_start); 32201da177e4SLinus Torvalds 32211da177e4SLinus Torvalds void *neigh_seq_next(struct seq_file *seq, void *v, loff_t *pos) 32221da177e4SLinus Torvalds { 32231da177e4SLinus Torvalds struct neigh_seq_state *state; 32241da177e4SLinus Torvalds void *rc; 32251da177e4SLinus Torvalds 32261da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 3227bff69732SChris Larson rc = neigh_get_first(seq); 32281da177e4SLinus Torvalds goto out; 32291da177e4SLinus Torvalds } 32301da177e4SLinus Torvalds 32311da177e4SLinus Torvalds state = seq->private; 32321da177e4SLinus Torvalds if (!(state->flags & NEIGH_SEQ_IS_PNEIGH)) { 32331da177e4SLinus Torvalds rc = neigh_get_next(seq, v, NULL); 32341da177e4SLinus Torvalds if (rc) 32351da177e4SLinus Torvalds goto out; 32361da177e4SLinus Torvalds if (!(state->flags & NEIGH_SEQ_NEIGH_ONLY)) 32371da177e4SLinus Torvalds rc = pneigh_get_first(seq); 32381da177e4SLinus Torvalds } else { 32391da177e4SLinus Torvalds BUG_ON(state->flags & NEIGH_SEQ_NEIGH_ONLY); 32401da177e4SLinus Torvalds rc = pneigh_get_next(seq, v, NULL); 32411da177e4SLinus Torvalds } 32421da177e4SLinus Torvalds out: 32431da177e4SLinus Torvalds ++(*pos); 32441da177e4SLinus Torvalds return rc; 32451da177e4SLinus Torvalds } 32461da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_seq_next); 32471da177e4SLinus Torvalds 32481da177e4SLinus Torvalds void neigh_seq_stop(struct seq_file *seq, void *v) 3249d6bf7817SEric Dumazet __releases(rcu_bh) 32501da177e4SLinus Torvalds { 3251d6bf7817SEric Dumazet rcu_read_unlock_bh(); 32521da177e4SLinus Torvalds } 32531da177e4SLinus Torvalds EXPORT_SYMBOL(neigh_seq_stop); 32541da177e4SLinus Torvalds 32551da177e4SLinus Torvalds /* statistics via seq_file */ 32561da177e4SLinus Torvalds 32571da177e4SLinus Torvalds static void *neigh_stat_seq_start(struct seq_file *seq, loff_t *pos) 32581da177e4SLinus Torvalds { 325971a5053aSChristoph Hellwig struct neigh_table *tbl = PDE_DATA(file_inode(seq->file)); 32601da177e4SLinus Torvalds int cpu; 32611da177e4SLinus Torvalds 32621da177e4SLinus Torvalds if (*pos == 0) 32631da177e4SLinus Torvalds return SEQ_START_TOKEN; 32641da177e4SLinus Torvalds 32650f23174aSRusty Russell for (cpu = *pos-1; cpu < nr_cpu_ids; ++cpu) { 32661da177e4SLinus Torvalds if (!cpu_possible(cpu)) 32671da177e4SLinus Torvalds continue; 32681da177e4SLinus Torvalds *pos = cpu+1; 32691da177e4SLinus Torvalds return per_cpu_ptr(tbl->stats, cpu); 32701da177e4SLinus Torvalds } 32711da177e4SLinus Torvalds return NULL; 32721da177e4SLinus Torvalds } 32731da177e4SLinus Torvalds 32741da177e4SLinus Torvalds static void *neigh_stat_seq_next(struct seq_file *seq, void *v, loff_t *pos) 32751da177e4SLinus Torvalds { 327671a5053aSChristoph Hellwig struct neigh_table *tbl = PDE_DATA(file_inode(seq->file)); 32771da177e4SLinus Torvalds int cpu; 32781da177e4SLinus Torvalds 32790f23174aSRusty Russell for (cpu = *pos; cpu < nr_cpu_ids; ++cpu) { 32801da177e4SLinus Torvalds if (!cpu_possible(cpu)) 32811da177e4SLinus Torvalds continue; 32821da177e4SLinus Torvalds *pos = cpu+1; 32831da177e4SLinus Torvalds return per_cpu_ptr(tbl->stats, cpu); 32841da177e4SLinus Torvalds } 32851da177e4SLinus Torvalds return NULL; 32861da177e4SLinus Torvalds } 32871da177e4SLinus Torvalds 32881da177e4SLinus Torvalds static void neigh_stat_seq_stop(struct seq_file *seq, void *v) 32891da177e4SLinus Torvalds { 32901da177e4SLinus Torvalds 32911da177e4SLinus Torvalds } 32921da177e4SLinus Torvalds 32931da177e4SLinus Torvalds static int neigh_stat_seq_show(struct seq_file *seq, void *v) 32941da177e4SLinus Torvalds { 329571a5053aSChristoph Hellwig struct neigh_table *tbl = PDE_DATA(file_inode(seq->file)); 32961da177e4SLinus Torvalds struct neigh_statistics *st = v; 32971da177e4SLinus Torvalds 32981da177e4SLinus Torvalds if (v == SEQ_START_TOKEN) { 3299fb811395SRick Jones seq_printf(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"); 33001da177e4SLinus Torvalds return 0; 33011da177e4SLinus Torvalds } 33021da177e4SLinus Torvalds 33031da177e4SLinus Torvalds seq_printf(seq, "%08x %08lx %08lx %08lx %08lx %08lx %08lx " 3304fb811395SRick Jones "%08lx %08lx %08lx %08lx %08lx %08lx\n", 33051da177e4SLinus Torvalds atomic_read(&tbl->entries), 33061da177e4SLinus Torvalds 33071da177e4SLinus Torvalds st->allocs, 33081da177e4SLinus Torvalds st->destroys, 33091da177e4SLinus Torvalds st->hash_grows, 33101da177e4SLinus Torvalds 33111da177e4SLinus Torvalds st->lookups, 33121da177e4SLinus Torvalds st->hits, 33131da177e4SLinus Torvalds 33141da177e4SLinus Torvalds st->res_failed, 33151da177e4SLinus Torvalds 33161da177e4SLinus Torvalds st->rcv_probes_mcast, 33171da177e4SLinus Torvalds st->rcv_probes_ucast, 33181da177e4SLinus Torvalds 33191da177e4SLinus Torvalds st->periodic_gc_runs, 33209a6d276eSNeil Horman st->forced_gc_runs, 3321fb811395SRick Jones st->unres_discards, 3322fb811395SRick Jones st->table_fulls 33231da177e4SLinus Torvalds ); 33241da177e4SLinus Torvalds 33251da177e4SLinus Torvalds return 0; 33261da177e4SLinus Torvalds } 33271da177e4SLinus Torvalds 3328f690808eSStephen Hemminger static const struct seq_operations neigh_stat_seq_ops = { 33291da177e4SLinus Torvalds .start = neigh_stat_seq_start, 33301da177e4SLinus Torvalds .next = neigh_stat_seq_next, 33311da177e4SLinus Torvalds .stop = neigh_stat_seq_stop, 33321da177e4SLinus Torvalds .show = neigh_stat_seq_show, 33331da177e4SLinus Torvalds }; 33341da177e4SLinus Torvalds #endif /* CONFIG_PROC_FS */ 33351da177e4SLinus Torvalds 33367b8f7a40SRoopa Prabhu static void __neigh_notify(struct neighbour *n, int type, int flags, 33377b8f7a40SRoopa Prabhu u32 pid) 33381da177e4SLinus Torvalds { 3339c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(n->dev); 33408b8aec50SThomas Graf struct sk_buff *skb; 3341b8673311SThomas Graf int err = -ENOBUFS; 33421da177e4SLinus Torvalds 3343339bf98fSThomas Graf skb = nlmsg_new(neigh_nlmsg_size(), GFP_ATOMIC); 33448b8aec50SThomas Graf if (skb == NULL) 3345b8673311SThomas Graf goto errout; 33461da177e4SLinus Torvalds 33477b8f7a40SRoopa Prabhu err = neigh_fill_info(skb, n, pid, 0, type, flags); 334826932566SPatrick McHardy if (err < 0) { 334926932566SPatrick McHardy /* -EMSGSIZE implies BUG in neigh_nlmsg_size() */ 335026932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 335126932566SPatrick McHardy kfree_skb(skb); 335226932566SPatrick McHardy goto errout; 335326932566SPatrick McHardy } 33541ce85fe4SPablo Neira Ayuso rtnl_notify(skb, net, 0, RTNLGRP_NEIGH, NULL, GFP_ATOMIC); 33551ce85fe4SPablo Neira Ayuso return; 3356b8673311SThomas Graf errout: 3357b8673311SThomas Graf if (err < 0) 3358426b5303SEric W. Biederman rtnl_set_sk_err(net, RTNLGRP_NEIGH, err); 3359b8673311SThomas Graf } 3360b8673311SThomas Graf 3361b8673311SThomas Graf void neigh_app_ns(struct neighbour *n) 3362b8673311SThomas Graf { 33637b8f7a40SRoopa Prabhu __neigh_notify(n, RTM_GETNEIGH, NLM_F_REQUEST, 0); 33648b8aec50SThomas Graf } 33650a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_app_ns); 33661da177e4SLinus Torvalds 33671da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 3368b93196dcSCong Wang static int zero; 3369555445cdSFrancesco Fusco static int int_max = INT_MAX; 3370b93196dcSCong Wang static int unres_qlen_max = INT_MAX / SKB_TRUESIZE(ETH_FRAME_LEN); 33711da177e4SLinus Torvalds 3372fe2c6338SJoe Perches static int proc_unres_qlen(struct ctl_table *ctl, int write, 3373fe2c6338SJoe Perches void __user *buffer, size_t *lenp, loff_t *ppos) 33748b5c171bSEric Dumazet { 33758b5c171bSEric Dumazet int size, ret; 3376fe2c6338SJoe Perches struct ctl_table tmp = *ctl; 33778b5c171bSEric Dumazet 3378ce46cc64SShan Wei tmp.extra1 = &zero; 3379ce46cc64SShan Wei tmp.extra2 = &unres_qlen_max; 33808b5c171bSEric Dumazet tmp.data = &size; 3381ce46cc64SShan Wei 3382ce46cc64SShan Wei size = *(int *)ctl->data / SKB_TRUESIZE(ETH_FRAME_LEN); 3383ce46cc64SShan Wei ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); 3384ce46cc64SShan Wei 33858b5c171bSEric Dumazet if (write && !ret) 33868b5c171bSEric Dumazet *(int *)ctl->data = size * SKB_TRUESIZE(ETH_FRAME_LEN); 33878b5c171bSEric Dumazet return ret; 33888b5c171bSEric Dumazet } 33898b5c171bSEric Dumazet 33901d4c8c29SJiri Pirko static struct neigh_parms *neigh_get_dev_parms_rcu(struct net_device *dev, 33911d4c8c29SJiri Pirko int family) 33921d4c8c29SJiri Pirko { 3393bba24896SJiri Pirko switch (family) { 3394bba24896SJiri Pirko case AF_INET: 33951d4c8c29SJiri Pirko return __in_dev_arp_parms_get_rcu(dev); 3396bba24896SJiri Pirko case AF_INET6: 3397bba24896SJiri Pirko return __in6_dev_nd_parms_get_rcu(dev); 3398bba24896SJiri Pirko } 33991d4c8c29SJiri Pirko return NULL; 34001d4c8c29SJiri Pirko } 34011d4c8c29SJiri Pirko 34021d4c8c29SJiri Pirko static void neigh_copy_dflt_parms(struct net *net, struct neigh_parms *p, 34031d4c8c29SJiri Pirko int index) 34041d4c8c29SJiri Pirko { 34051d4c8c29SJiri Pirko struct net_device *dev; 34061d4c8c29SJiri Pirko int family = neigh_parms_family(p); 34071d4c8c29SJiri Pirko 34081d4c8c29SJiri Pirko rcu_read_lock(); 34091d4c8c29SJiri Pirko for_each_netdev_rcu(net, dev) { 34101d4c8c29SJiri Pirko struct neigh_parms *dst_p = 34111d4c8c29SJiri Pirko neigh_get_dev_parms_rcu(dev, family); 34121d4c8c29SJiri Pirko 34131d4c8c29SJiri Pirko if (dst_p && !test_bit(index, dst_p->data_state)) 34141d4c8c29SJiri Pirko dst_p->data[index] = p->data[index]; 34151d4c8c29SJiri Pirko } 34161d4c8c29SJiri Pirko rcu_read_unlock(); 34171d4c8c29SJiri Pirko } 34181d4c8c29SJiri Pirko 34191d4c8c29SJiri Pirko static void neigh_proc_update(struct ctl_table *ctl, int write) 34201d4c8c29SJiri Pirko { 34211d4c8c29SJiri Pirko struct net_device *dev = ctl->extra1; 34221d4c8c29SJiri Pirko struct neigh_parms *p = ctl->extra2; 342377d47afbSJiri Pirko struct net *net = neigh_parms_net(p); 34241d4c8c29SJiri Pirko int index = (int *) ctl->data - p->data; 34251d4c8c29SJiri Pirko 34261d4c8c29SJiri Pirko if (!write) 34271d4c8c29SJiri Pirko return; 34281d4c8c29SJiri Pirko 34291d4c8c29SJiri Pirko set_bit(index, p->data_state); 34307627ae60SMarcus Huewe if (index == NEIGH_VAR_DELAY_PROBE_TIME) 34312a4501aeSIdo Schimmel call_netevent_notifiers(NETEVENT_DELAY_PROBE_TIME_UPDATE, p); 34321d4c8c29SJiri Pirko if (!dev) /* NULL dev means this is default value */ 34331d4c8c29SJiri Pirko neigh_copy_dflt_parms(net, p, index); 34341d4c8c29SJiri Pirko } 34351d4c8c29SJiri Pirko 34361f9248e5SJiri Pirko static int neigh_proc_dointvec_zero_intmax(struct ctl_table *ctl, int write, 34371f9248e5SJiri Pirko void __user *buffer, 34381f9248e5SJiri Pirko size_t *lenp, loff_t *ppos) 34391f9248e5SJiri Pirko { 34401f9248e5SJiri Pirko struct ctl_table tmp = *ctl; 34411d4c8c29SJiri Pirko int ret; 34421f9248e5SJiri Pirko 34431f9248e5SJiri Pirko tmp.extra1 = &zero; 34441f9248e5SJiri Pirko tmp.extra2 = &int_max; 34451f9248e5SJiri Pirko 34461d4c8c29SJiri Pirko ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); 34471d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 34481d4c8c29SJiri Pirko return ret; 34491f9248e5SJiri Pirko } 34501f9248e5SJiri Pirko 3451cb5b09c1SJiri Pirko int neigh_proc_dointvec(struct ctl_table *ctl, int write, 3452cb5b09c1SJiri Pirko void __user *buffer, size_t *lenp, loff_t *ppos) 3453cb5b09c1SJiri Pirko { 34541d4c8c29SJiri Pirko int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 34551d4c8c29SJiri Pirko 34561d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 34571d4c8c29SJiri Pirko return ret; 3458cb5b09c1SJiri Pirko } 3459cb5b09c1SJiri Pirko EXPORT_SYMBOL(neigh_proc_dointvec); 3460cb5b09c1SJiri Pirko 3461cb5b09c1SJiri Pirko int neigh_proc_dointvec_jiffies(struct ctl_table *ctl, int write, 3462cb5b09c1SJiri Pirko void __user *buffer, 3463cb5b09c1SJiri Pirko size_t *lenp, loff_t *ppos) 3464cb5b09c1SJiri Pirko { 34651d4c8c29SJiri Pirko int ret = proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos); 34661d4c8c29SJiri Pirko 34671d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 34681d4c8c29SJiri Pirko return ret; 3469cb5b09c1SJiri Pirko } 3470cb5b09c1SJiri Pirko EXPORT_SYMBOL(neigh_proc_dointvec_jiffies); 3471cb5b09c1SJiri Pirko 3472cb5b09c1SJiri Pirko static int neigh_proc_dointvec_userhz_jiffies(struct ctl_table *ctl, int write, 3473cb5b09c1SJiri Pirko void __user *buffer, 3474cb5b09c1SJiri Pirko size_t *lenp, loff_t *ppos) 3475cb5b09c1SJiri Pirko { 34761d4c8c29SJiri Pirko int ret = proc_dointvec_userhz_jiffies(ctl, write, buffer, lenp, ppos); 34771d4c8c29SJiri Pirko 34781d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 34791d4c8c29SJiri Pirko return ret; 3480cb5b09c1SJiri Pirko } 3481cb5b09c1SJiri Pirko 3482cb5b09c1SJiri Pirko int neigh_proc_dointvec_ms_jiffies(struct ctl_table *ctl, int write, 3483cb5b09c1SJiri Pirko void __user *buffer, 3484cb5b09c1SJiri Pirko size_t *lenp, loff_t *ppos) 3485cb5b09c1SJiri Pirko { 34861d4c8c29SJiri Pirko int ret = proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos); 34871d4c8c29SJiri Pirko 34881d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 34891d4c8c29SJiri Pirko return ret; 3490cb5b09c1SJiri Pirko } 3491cb5b09c1SJiri Pirko EXPORT_SYMBOL(neigh_proc_dointvec_ms_jiffies); 3492cb5b09c1SJiri Pirko 3493cb5b09c1SJiri Pirko static int neigh_proc_dointvec_unres_qlen(struct ctl_table *ctl, int write, 3494cb5b09c1SJiri Pirko void __user *buffer, 3495cb5b09c1SJiri Pirko size_t *lenp, loff_t *ppos) 3496cb5b09c1SJiri Pirko { 34971d4c8c29SJiri Pirko int ret = proc_unres_qlen(ctl, write, buffer, lenp, ppos); 34981d4c8c29SJiri Pirko 34991d4c8c29SJiri Pirko neigh_proc_update(ctl, write); 35001d4c8c29SJiri Pirko return ret; 3501cb5b09c1SJiri Pirko } 3502cb5b09c1SJiri Pirko 35034bf6980dSJean-Francois Remy static int neigh_proc_base_reachable_time(struct ctl_table *ctl, int write, 35044bf6980dSJean-Francois Remy void __user *buffer, 35054bf6980dSJean-Francois Remy size_t *lenp, loff_t *ppos) 35064bf6980dSJean-Francois Remy { 35074bf6980dSJean-Francois Remy struct neigh_parms *p = ctl->extra2; 35084bf6980dSJean-Francois Remy int ret; 35094bf6980dSJean-Francois Remy 35104bf6980dSJean-Francois Remy if (strcmp(ctl->procname, "base_reachable_time") == 0) 35114bf6980dSJean-Francois Remy ret = neigh_proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos); 35124bf6980dSJean-Francois Remy else if (strcmp(ctl->procname, "base_reachable_time_ms") == 0) 35134bf6980dSJean-Francois Remy ret = neigh_proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos); 35144bf6980dSJean-Francois Remy else 35154bf6980dSJean-Francois Remy ret = -1; 35164bf6980dSJean-Francois Remy 35174bf6980dSJean-Francois Remy if (write && ret == 0) { 35184bf6980dSJean-Francois Remy /* update reachable_time as well, otherwise, the change will 35194bf6980dSJean-Francois Remy * only be effective after the next time neigh_periodic_work 35204bf6980dSJean-Francois Remy * decides to recompute it 35214bf6980dSJean-Francois Remy */ 35224bf6980dSJean-Francois Remy p->reachable_time = 35234bf6980dSJean-Francois Remy neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME)); 35244bf6980dSJean-Francois Remy } 35254bf6980dSJean-Francois Remy return ret; 35264bf6980dSJean-Francois Remy } 35274bf6980dSJean-Francois Remy 35281f9248e5SJiri Pirko #define NEIGH_PARMS_DATA_OFFSET(index) \ 35291f9248e5SJiri Pirko (&((struct neigh_parms *) 0)->data[index]) 35301f9248e5SJiri Pirko 35311f9248e5SJiri Pirko #define NEIGH_SYSCTL_ENTRY(attr, data_attr, name, mval, proc) \ 35321f9248e5SJiri Pirko [NEIGH_VAR_ ## attr] = { \ 35331f9248e5SJiri Pirko .procname = name, \ 35341f9248e5SJiri Pirko .data = NEIGH_PARMS_DATA_OFFSET(NEIGH_VAR_ ## data_attr), \ 35351f9248e5SJiri Pirko .maxlen = sizeof(int), \ 35361f9248e5SJiri Pirko .mode = mval, \ 35371f9248e5SJiri Pirko .proc_handler = proc, \ 35381f9248e5SJiri Pirko } 35391f9248e5SJiri Pirko 35401f9248e5SJiri Pirko #define NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(attr, name) \ 35411f9248e5SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_zero_intmax) 35421f9248e5SJiri Pirko 35431f9248e5SJiri Pirko #define NEIGH_SYSCTL_JIFFIES_ENTRY(attr, name) \ 3544cb5b09c1SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_jiffies) 35451f9248e5SJiri Pirko 35461f9248e5SJiri Pirko #define NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(attr, name) \ 3547cb5b09c1SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_userhz_jiffies) 35481f9248e5SJiri Pirko 35491f9248e5SJiri Pirko #define NEIGH_SYSCTL_MS_JIFFIES_ENTRY(attr, name) \ 3550cb5b09c1SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_ms_jiffies) 35511f9248e5SJiri Pirko 35521f9248e5SJiri Pirko #define NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(attr, data_attr, name) \ 3553cb5b09c1SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_ms_jiffies) 35541f9248e5SJiri Pirko 35551f9248e5SJiri Pirko #define NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(attr, data_attr, name) \ 3556cb5b09c1SJiri Pirko NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_unres_qlen) 355754716e3bSEric W. Biederman 35581da177e4SLinus Torvalds static struct neigh_sysctl_table { 35591da177e4SLinus Torvalds struct ctl_table_header *sysctl_header; 35608b5c171bSEric Dumazet struct ctl_table neigh_vars[NEIGH_VAR_MAX + 1]; 3561ab32ea5dSBrian Haley } neigh_sysctl_template __read_mostly = { 35621da177e4SLinus Torvalds .neigh_vars = { 35631f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(MCAST_PROBES, "mcast_solicit"), 35641f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(UCAST_PROBES, "ucast_solicit"), 35651f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(APP_PROBES, "app_solicit"), 35668da86466SYOSHIFUJI Hideaki/吉藤英明 NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(MCAST_REPROBES, "mcast_resolicit"), 35671f9248e5SJiri Pirko NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(RETRANS_TIME, "retrans_time"), 35681f9248e5SJiri Pirko NEIGH_SYSCTL_JIFFIES_ENTRY(BASE_REACHABLE_TIME, "base_reachable_time"), 35691f9248e5SJiri Pirko NEIGH_SYSCTL_JIFFIES_ENTRY(DELAY_PROBE_TIME, "delay_first_probe_time"), 35701f9248e5SJiri Pirko NEIGH_SYSCTL_JIFFIES_ENTRY(GC_STALETIME, "gc_stale_time"), 35711f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(QUEUE_LEN_BYTES, "unres_qlen_bytes"), 35721f9248e5SJiri Pirko NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(PROXY_QLEN, "proxy_qlen"), 35731f9248e5SJiri Pirko NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(ANYCAST_DELAY, "anycast_delay"), 35741f9248e5SJiri Pirko NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(PROXY_DELAY, "proxy_delay"), 35751f9248e5SJiri Pirko NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(LOCKTIME, "locktime"), 35761f9248e5SJiri Pirko NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(QUEUE_LEN, QUEUE_LEN_BYTES, "unres_qlen"), 35771f9248e5SJiri Pirko NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(RETRANS_TIME_MS, RETRANS_TIME, "retrans_time_ms"), 35781f9248e5SJiri Pirko NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(BASE_REACHABLE_TIME_MS, BASE_REACHABLE_TIME, "base_reachable_time_ms"), 35798b5c171bSEric Dumazet [NEIGH_VAR_GC_INTERVAL] = { 35801da177e4SLinus Torvalds .procname = "gc_interval", 35811da177e4SLinus Torvalds .maxlen = sizeof(int), 35821da177e4SLinus Torvalds .mode = 0644, 35836d9f239aSAlexey Dobriyan .proc_handler = proc_dointvec_jiffies, 35841da177e4SLinus Torvalds }, 35858b5c171bSEric Dumazet [NEIGH_VAR_GC_THRESH1] = { 35861da177e4SLinus Torvalds .procname = "gc_thresh1", 35871da177e4SLinus Torvalds .maxlen = sizeof(int), 35881da177e4SLinus Torvalds .mode = 0644, 3589555445cdSFrancesco Fusco .extra1 = &zero, 3590555445cdSFrancesco Fusco .extra2 = &int_max, 3591555445cdSFrancesco Fusco .proc_handler = proc_dointvec_minmax, 35921da177e4SLinus Torvalds }, 35938b5c171bSEric Dumazet [NEIGH_VAR_GC_THRESH2] = { 35941da177e4SLinus Torvalds .procname = "gc_thresh2", 35951da177e4SLinus Torvalds .maxlen = sizeof(int), 35961da177e4SLinus Torvalds .mode = 0644, 3597555445cdSFrancesco Fusco .extra1 = &zero, 3598555445cdSFrancesco Fusco .extra2 = &int_max, 3599555445cdSFrancesco Fusco .proc_handler = proc_dointvec_minmax, 36001da177e4SLinus Torvalds }, 36018b5c171bSEric Dumazet [NEIGH_VAR_GC_THRESH3] = { 36021da177e4SLinus Torvalds .procname = "gc_thresh3", 36031da177e4SLinus Torvalds .maxlen = sizeof(int), 36041da177e4SLinus Torvalds .mode = 0644, 3605555445cdSFrancesco Fusco .extra1 = &zero, 3606555445cdSFrancesco Fusco .extra2 = &int_max, 3607555445cdSFrancesco Fusco .proc_handler = proc_dointvec_minmax, 36081da177e4SLinus Torvalds }, 3609c3bac5a7SPavel Emelyanov {}, 36101da177e4SLinus Torvalds }, 36111da177e4SLinus Torvalds }; 36121da177e4SLinus Torvalds 36131da177e4SLinus Torvalds int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p, 361473af614aSJiri Pirko proc_handler *handler) 36151da177e4SLinus Torvalds { 36161f9248e5SJiri Pirko int i; 36173c607bbbSPavel Emelyanov struct neigh_sysctl_table *t; 36181f9248e5SJiri Pirko const char *dev_name_source; 36198f40a1f9SEric W. Biederman char neigh_path[ sizeof("net//neigh/") + IFNAMSIZ + IFNAMSIZ ]; 362073af614aSJiri Pirko char *p_name; 36211da177e4SLinus Torvalds 36223c607bbbSPavel Emelyanov t = kmemdup(&neigh_sysctl_template, sizeof(*t), GFP_KERNEL); 36231da177e4SLinus Torvalds if (!t) 36243c607bbbSPavel Emelyanov goto err; 36253c607bbbSPavel Emelyanov 3626b194c1f1SJiri Pirko for (i = 0; i < NEIGH_VAR_GC_INTERVAL; i++) { 36271f9248e5SJiri Pirko t->neigh_vars[i].data += (long) p; 3628cb5b09c1SJiri Pirko t->neigh_vars[i].extra1 = dev; 36291d4c8c29SJiri Pirko t->neigh_vars[i].extra2 = p; 3630cb5b09c1SJiri Pirko } 36311da177e4SLinus Torvalds 36321da177e4SLinus Torvalds if (dev) { 36331da177e4SLinus Torvalds dev_name_source = dev->name; 3634d12af679SEric W. Biederman /* Terminate the table early */ 36358b5c171bSEric Dumazet memset(&t->neigh_vars[NEIGH_VAR_GC_INTERVAL], 0, 36368b5c171bSEric Dumazet sizeof(t->neigh_vars[NEIGH_VAR_GC_INTERVAL])); 36371da177e4SLinus Torvalds } else { 36389ecf07a1SMathias Krause struct neigh_table *tbl = p->tbl; 36398f40a1f9SEric W. Biederman dev_name_source = "default"; 36409ecf07a1SMathias Krause t->neigh_vars[NEIGH_VAR_GC_INTERVAL].data = &tbl->gc_interval; 36419ecf07a1SMathias Krause t->neigh_vars[NEIGH_VAR_GC_THRESH1].data = &tbl->gc_thresh1; 36429ecf07a1SMathias Krause t->neigh_vars[NEIGH_VAR_GC_THRESH2].data = &tbl->gc_thresh2; 36439ecf07a1SMathias Krause t->neigh_vars[NEIGH_VAR_GC_THRESH3].data = &tbl->gc_thresh3; 36441da177e4SLinus Torvalds } 36451da177e4SLinus Torvalds 3646f8572d8fSEric W. Biederman if (handler) { 36471da177e4SLinus Torvalds /* RetransTime */ 36488b5c171bSEric Dumazet t->neigh_vars[NEIGH_VAR_RETRANS_TIME].proc_handler = handler; 36491da177e4SLinus Torvalds /* ReachableTime */ 36508b5c171bSEric Dumazet t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].proc_handler = handler; 36511da177e4SLinus Torvalds /* RetransTime (in milliseconds)*/ 36528b5c171bSEric Dumazet t->neigh_vars[NEIGH_VAR_RETRANS_TIME_MS].proc_handler = handler; 36531da177e4SLinus Torvalds /* ReachableTime (in milliseconds) */ 36548b5c171bSEric Dumazet t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].proc_handler = handler; 36554bf6980dSJean-Francois Remy } else { 36564bf6980dSJean-Francois Remy /* Those handlers will update p->reachable_time after 36574bf6980dSJean-Francois Remy * base_reachable_time(_ms) is set to ensure the new timer starts being 36584bf6980dSJean-Francois Remy * applied after the next neighbour update instead of waiting for 36594bf6980dSJean-Francois Remy * neigh_periodic_work to update its value (can be multiple minutes) 36604bf6980dSJean-Francois Remy * So any handler that replaces them should do this as well 36614bf6980dSJean-Francois Remy */ 36624bf6980dSJean-Francois Remy /* ReachableTime */ 36634bf6980dSJean-Francois Remy t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].proc_handler = 36644bf6980dSJean-Francois Remy neigh_proc_base_reachable_time; 36654bf6980dSJean-Francois Remy /* ReachableTime (in milliseconds) */ 36664bf6980dSJean-Francois Remy t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].proc_handler = 36674bf6980dSJean-Francois Remy neigh_proc_base_reachable_time; 36681da177e4SLinus Torvalds } 36691da177e4SLinus Torvalds 3670464dc801SEric W. Biederman /* Don't export sysctls to unprivileged users */ 3671464dc801SEric W. Biederman if (neigh_parms_net(p)->user_ns != &init_user_ns) 3672464dc801SEric W. Biederman t->neigh_vars[0].procname = NULL; 3673464dc801SEric W. Biederman 367473af614aSJiri Pirko switch (neigh_parms_family(p)) { 367573af614aSJiri Pirko case AF_INET: 367673af614aSJiri Pirko p_name = "ipv4"; 367773af614aSJiri Pirko break; 367873af614aSJiri Pirko case AF_INET6: 367973af614aSJiri Pirko p_name = "ipv6"; 368073af614aSJiri Pirko break; 368173af614aSJiri Pirko default: 368273af614aSJiri Pirko BUG(); 368373af614aSJiri Pirko } 368473af614aSJiri Pirko 36858f40a1f9SEric W. Biederman snprintf(neigh_path, sizeof(neigh_path), "net/%s/neigh/%s", 36868f40a1f9SEric W. Biederman p_name, dev_name_source); 36874ab438fcSDenis V. Lunev t->sysctl_header = 36888f40a1f9SEric W. Biederman register_net_sysctl(neigh_parms_net(p), neigh_path, t->neigh_vars); 36893c607bbbSPavel Emelyanov if (!t->sysctl_header) 36908f40a1f9SEric W. Biederman goto free; 36913c607bbbSPavel Emelyanov 36921da177e4SLinus Torvalds p->sysctl_table = t; 36931da177e4SLinus Torvalds return 0; 36941da177e4SLinus Torvalds 36951da177e4SLinus Torvalds free: 36961da177e4SLinus Torvalds kfree(t); 36973c607bbbSPavel Emelyanov err: 36983c607bbbSPavel Emelyanov return -ENOBUFS; 36991da177e4SLinus Torvalds } 37000a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_sysctl_register); 37011da177e4SLinus Torvalds 37021da177e4SLinus Torvalds void neigh_sysctl_unregister(struct neigh_parms *p) 37031da177e4SLinus Torvalds { 37041da177e4SLinus Torvalds if (p->sysctl_table) { 37051da177e4SLinus Torvalds struct neigh_sysctl_table *t = p->sysctl_table; 37061da177e4SLinus Torvalds p->sysctl_table = NULL; 37075dd3df10SEric W. Biederman unregister_net_sysctl_table(t->sysctl_header); 37081da177e4SLinus Torvalds kfree(t); 37091da177e4SLinus Torvalds } 37101da177e4SLinus Torvalds } 37110a204500SYOSHIFUJI Hideaki EXPORT_SYMBOL(neigh_sysctl_unregister); 37121da177e4SLinus Torvalds 37131da177e4SLinus Torvalds #endif /* CONFIG_SYSCTL */ 37141da177e4SLinus Torvalds 3715c8822a4eSThomas Graf static int __init neigh_init(void) 3716c8822a4eSThomas Graf { 3717b97bac64SFlorian Westphal rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL, 0); 3718b97bac64SFlorian Westphal rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL, 0); 371982cbb5c6SRoopa Prabhu rtnl_register(PF_UNSPEC, RTM_GETNEIGH, neigh_get, neigh_dump_info, 0); 3720c8822a4eSThomas Graf 3721c7ac8679SGreg Rose rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info, 3722b97bac64SFlorian Westphal 0); 3723b97bac64SFlorian Westphal rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL, 0); 3724c8822a4eSThomas Graf 3725c8822a4eSThomas Graf return 0; 3726c8822a4eSThomas Graf } 3727c8822a4eSThomas Graf 3728c8822a4eSThomas Graf subsys_initcall(neigh_init); 3729