.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Generic address resolution entity |
---|
3 | 4 | * |
---|
4 | 5 | * Authors: |
---|
5 | 6 | * Pedro Roque <roque@di.fc.ul.pt> |
---|
6 | 7 | * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> |
---|
7 | | - * |
---|
8 | | - * This program is free software; you can redistribute it and/or |
---|
9 | | - * modify it under the terms of the GNU General Public License |
---|
10 | | - * as published by the Free Software Foundation; either version |
---|
11 | | - * 2 of the License, or (at your option) any later version. |
---|
12 | 8 | * |
---|
13 | 9 | * Fixes: |
---|
14 | 10 | * Vitaly E. Lavrov releasing NULL neighbor in neigh_add. |
---|
.. | .. |
---|
42 | 38 | #include <linux/log2.h> |
---|
43 | 39 | #include <linux/inetdevice.h> |
---|
44 | 40 | #include <net/addrconf.h> |
---|
| 41 | + |
---|
| 42 | +#include <trace/events/neigh.h> |
---|
45 | 43 | |
---|
46 | 44 | #define DEBUG |
---|
47 | 45 | #define NEIGH_DEBUG 1 |
---|
.. | .. |
---|
100 | 98 | |
---|
101 | 99 | static void neigh_cleanup_and_release(struct neighbour *neigh) |
---|
102 | 100 | { |
---|
103 | | - if (neigh->parms->neigh_cleanup) |
---|
104 | | - neigh->parms->neigh_cleanup(neigh); |
---|
105 | | - |
---|
| 101 | + trace_neigh_cleanup_and_release(neigh, 0); |
---|
106 | 102 | __neigh_notify(neigh, RTM_DELNEIGH, 0, 0); |
---|
107 | 103 | call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, neigh); |
---|
108 | 104 | neigh_release(neigh); |
---|
.. | .. |
---|
120 | 116 | } |
---|
121 | 117 | EXPORT_SYMBOL(neigh_rand_reach_time); |
---|
122 | 118 | |
---|
| 119 | +static void neigh_mark_dead(struct neighbour *n) |
---|
| 120 | +{ |
---|
| 121 | + n->dead = 1; |
---|
| 122 | + if (!list_empty(&n->gc_list)) { |
---|
| 123 | + list_del_init(&n->gc_list); |
---|
| 124 | + atomic_dec(&n->tbl->gc_entries); |
---|
| 125 | + } |
---|
| 126 | +} |
---|
123 | 127 | |
---|
124 | | -static bool neigh_del(struct neighbour *n, __u8 state, __u8 flags, |
---|
125 | | - struct neighbour __rcu **np, struct neigh_table *tbl) |
---|
| 128 | +static void neigh_update_gc_list(struct neighbour *n) |
---|
| 129 | +{ |
---|
| 130 | + bool on_gc_list, exempt_from_gc; |
---|
| 131 | + |
---|
| 132 | + write_lock_bh(&n->tbl->lock); |
---|
| 133 | + write_lock(&n->lock); |
---|
| 134 | + |
---|
| 135 | + if (n->dead) |
---|
| 136 | + goto out; |
---|
| 137 | + |
---|
| 138 | + /* remove from the gc list if new state is permanent or if neighbor |
---|
| 139 | + * is externally learned; otherwise entry should be on the gc list |
---|
| 140 | + */ |
---|
| 141 | + exempt_from_gc = n->nud_state & NUD_PERMANENT || |
---|
| 142 | + n->flags & NTF_EXT_LEARNED; |
---|
| 143 | + on_gc_list = !list_empty(&n->gc_list); |
---|
| 144 | + |
---|
| 145 | + if (exempt_from_gc && on_gc_list) { |
---|
| 146 | + list_del_init(&n->gc_list); |
---|
| 147 | + atomic_dec(&n->tbl->gc_entries); |
---|
| 148 | + } else if (!exempt_from_gc && !on_gc_list) { |
---|
| 149 | + /* add entries to the tail; cleaning removes from the front */ |
---|
| 150 | + list_add_tail(&n->gc_list, &n->tbl->gc_list); |
---|
| 151 | + atomic_inc(&n->tbl->gc_entries); |
---|
| 152 | + } |
---|
| 153 | + |
---|
| 154 | +out: |
---|
| 155 | + write_unlock(&n->lock); |
---|
| 156 | + write_unlock_bh(&n->tbl->lock); |
---|
| 157 | +} |
---|
| 158 | + |
---|
| 159 | +static bool neigh_update_ext_learned(struct neighbour *neigh, u32 flags, |
---|
| 160 | + int *notify) |
---|
| 161 | +{ |
---|
| 162 | + bool rc = false; |
---|
| 163 | + u8 ndm_flags; |
---|
| 164 | + |
---|
| 165 | + if (!(flags & NEIGH_UPDATE_F_ADMIN)) |
---|
| 166 | + return rc; |
---|
| 167 | + |
---|
| 168 | + ndm_flags = (flags & NEIGH_UPDATE_F_EXT_LEARNED) ? NTF_EXT_LEARNED : 0; |
---|
| 169 | + if ((neigh->flags ^ ndm_flags) & NTF_EXT_LEARNED) { |
---|
| 170 | + if (ndm_flags & NTF_EXT_LEARNED) |
---|
| 171 | + neigh->flags |= NTF_EXT_LEARNED; |
---|
| 172 | + else |
---|
| 173 | + neigh->flags &= ~NTF_EXT_LEARNED; |
---|
| 174 | + rc = true; |
---|
| 175 | + *notify = 1; |
---|
| 176 | + } |
---|
| 177 | + |
---|
| 178 | + return rc; |
---|
| 179 | +} |
---|
| 180 | + |
---|
| 181 | +static bool neigh_del(struct neighbour *n, struct neighbour __rcu **np, |
---|
| 182 | + struct neigh_table *tbl) |
---|
126 | 183 | { |
---|
127 | 184 | bool retval = false; |
---|
128 | 185 | |
---|
129 | 186 | write_lock(&n->lock); |
---|
130 | | - if (refcount_read(&n->refcnt) == 1 && !(n->nud_state & state) && |
---|
131 | | - !(n->flags & flags)) { |
---|
| 187 | + if (refcount_read(&n->refcnt) == 1) { |
---|
132 | 188 | struct neighbour *neigh; |
---|
133 | 189 | |
---|
134 | 190 | neigh = rcu_dereference_protected(n->next, |
---|
135 | 191 | lockdep_is_held(&tbl->lock)); |
---|
136 | 192 | rcu_assign_pointer(*np, neigh); |
---|
137 | | - n->dead = 1; |
---|
| 193 | + neigh_mark_dead(n); |
---|
138 | 194 | retval = true; |
---|
139 | 195 | } |
---|
140 | 196 | write_unlock(&n->lock); |
---|
.. | .. |
---|
160 | 216 | while ((n = rcu_dereference_protected(*np, |
---|
161 | 217 | lockdep_is_held(&tbl->lock)))) { |
---|
162 | 218 | if (n == ndel) |
---|
163 | | - return neigh_del(n, 0, 0, np, tbl); |
---|
| 219 | + return neigh_del(n, np, tbl); |
---|
164 | 220 | np = &n->next; |
---|
165 | 221 | } |
---|
166 | 222 | return false; |
---|
.. | .. |
---|
168 | 224 | |
---|
169 | 225 | static int neigh_forced_gc(struct neigh_table *tbl) |
---|
170 | 226 | { |
---|
| 227 | + int max_clean = atomic_read(&tbl->gc_entries) - tbl->gc_thresh2; |
---|
| 228 | + unsigned long tref = jiffies - 5 * HZ; |
---|
| 229 | + struct neighbour *n, *tmp; |
---|
171 | 230 | int shrunk = 0; |
---|
172 | | - int i; |
---|
173 | | - struct neigh_hash_table *nht; |
---|
174 | 231 | |
---|
175 | 232 | NEIGH_CACHE_STAT_INC(tbl, forced_gc_runs); |
---|
176 | 233 | |
---|
177 | 234 | write_lock_bh(&tbl->lock); |
---|
178 | | - nht = rcu_dereference_protected(tbl->nht, |
---|
179 | | - lockdep_is_held(&tbl->lock)); |
---|
180 | | - for (i = 0; i < (1 << nht->hash_shift); i++) { |
---|
181 | | - struct neighbour *n; |
---|
182 | | - struct neighbour __rcu **np; |
---|
183 | 235 | |
---|
184 | | - np = &nht->hash_buckets[i]; |
---|
185 | | - while ((n = rcu_dereference_protected(*np, |
---|
186 | | - lockdep_is_held(&tbl->lock))) != NULL) { |
---|
187 | | - /* Neighbour record may be discarded if: |
---|
188 | | - * - nobody refers to it. |
---|
189 | | - * - it is not permanent |
---|
190 | | - */ |
---|
191 | | - if (neigh_del(n, NUD_PERMANENT, NTF_EXT_LEARNED, np, |
---|
192 | | - tbl)) { |
---|
193 | | - shrunk = 1; |
---|
194 | | - continue; |
---|
195 | | - } |
---|
196 | | - np = &n->next; |
---|
| 236 | + list_for_each_entry_safe(n, tmp, &tbl->gc_list, gc_list) { |
---|
| 237 | + if (refcount_read(&n->refcnt) == 1) { |
---|
| 238 | + bool remove = false; |
---|
| 239 | + |
---|
| 240 | + write_lock(&n->lock); |
---|
| 241 | + if ((n->nud_state == NUD_FAILED) || |
---|
| 242 | + (n->nud_state == NUD_NOARP) || |
---|
| 243 | + (tbl->is_multicast && |
---|
| 244 | + tbl->is_multicast(n->primary_key)) || |
---|
| 245 | + time_after(tref, n->updated)) |
---|
| 246 | + remove = true; |
---|
| 247 | + write_unlock(&n->lock); |
---|
| 248 | + |
---|
| 249 | + if (remove && neigh_remove_one(n, tbl)) |
---|
| 250 | + shrunk++; |
---|
| 251 | + if (shrunk >= max_clean) |
---|
| 252 | + break; |
---|
197 | 253 | } |
---|
198 | 254 | } |
---|
199 | 255 | |
---|
.. | .. |
---|
224 | 280 | return 0; |
---|
225 | 281 | } |
---|
226 | 282 | |
---|
227 | | -static void pneigh_queue_purge(struct sk_buff_head *list) |
---|
| 283 | +static void pneigh_queue_purge(struct sk_buff_head *list, struct net *net) |
---|
228 | 284 | { |
---|
| 285 | + struct sk_buff_head tmp; |
---|
| 286 | + unsigned long flags; |
---|
229 | 287 | struct sk_buff *skb; |
---|
230 | 288 | |
---|
231 | | - while ((skb = skb_dequeue(list)) != NULL) { |
---|
| 289 | + skb_queue_head_init(&tmp); |
---|
| 290 | + spin_lock_irqsave(&list->lock, flags); |
---|
| 291 | + skb = skb_peek(list); |
---|
| 292 | + while (skb != NULL) { |
---|
| 293 | + struct sk_buff *skb_next = skb_peek_next(skb, list); |
---|
| 294 | + if (net == NULL || net_eq(dev_net(skb->dev), net)) { |
---|
| 295 | + __skb_unlink(skb, list); |
---|
| 296 | + __skb_queue_tail(&tmp, skb); |
---|
| 297 | + } |
---|
| 298 | + skb = skb_next; |
---|
| 299 | + } |
---|
| 300 | + spin_unlock_irqrestore(&list->lock, flags); |
---|
| 301 | + |
---|
| 302 | + while ((skb = __skb_dequeue(&tmp))) { |
---|
232 | 303 | dev_put(skb->dev); |
---|
233 | 304 | kfree_skb(skb); |
---|
234 | 305 | } |
---|
235 | 306 | } |
---|
236 | 307 | |
---|
237 | | -static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev) |
---|
| 308 | +static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev, |
---|
| 309 | + bool skip_perm) |
---|
238 | 310 | { |
---|
239 | 311 | int i; |
---|
240 | 312 | struct neigh_hash_table *nht; |
---|
.. | .. |
---|
252 | 324 | np = &n->next; |
---|
253 | 325 | continue; |
---|
254 | 326 | } |
---|
| 327 | + if (skip_perm && n->nud_state & NUD_PERMANENT) { |
---|
| 328 | + np = &n->next; |
---|
| 329 | + continue; |
---|
| 330 | + } |
---|
255 | 331 | rcu_assign_pointer(*np, |
---|
256 | 332 | rcu_dereference_protected(n->next, |
---|
257 | 333 | lockdep_is_held(&tbl->lock))); |
---|
258 | 334 | write_lock(&n->lock); |
---|
259 | 335 | neigh_del_timer(n); |
---|
260 | | - n->dead = 1; |
---|
261 | | - |
---|
| 336 | + neigh_mark_dead(n); |
---|
262 | 337 | if (refcount_read(&n->refcnt) != 1) { |
---|
263 | 338 | /* The most unpleasant situation. |
---|
264 | 339 | We must destroy neighbour entry, |
---|
.. | .. |
---|
287 | 362 | void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev) |
---|
288 | 363 | { |
---|
289 | 364 | write_lock_bh(&tbl->lock); |
---|
290 | | - neigh_flush_dev(tbl, dev); |
---|
| 365 | + neigh_flush_dev(tbl, dev, false); |
---|
291 | 366 | write_unlock_bh(&tbl->lock); |
---|
292 | 367 | } |
---|
293 | 368 | EXPORT_SYMBOL(neigh_changeaddr); |
---|
294 | 369 | |
---|
295 | | -int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev) |
---|
| 370 | +static int __neigh_ifdown(struct neigh_table *tbl, struct net_device *dev, |
---|
| 371 | + bool skip_perm) |
---|
296 | 372 | { |
---|
297 | 373 | write_lock_bh(&tbl->lock); |
---|
298 | | - neigh_flush_dev(tbl, dev); |
---|
| 374 | + neigh_flush_dev(tbl, dev, skip_perm); |
---|
299 | 375 | pneigh_ifdown_and_unlock(tbl, dev); |
---|
| 376 | + pneigh_queue_purge(&tbl->proxy_queue, dev ? dev_net(dev) : NULL); |
---|
| 377 | + if (skb_queue_empty_lockless(&tbl->proxy_queue)) |
---|
| 378 | + del_timer_sync(&tbl->proxy_timer); |
---|
| 379 | + return 0; |
---|
| 380 | +} |
---|
300 | 381 | |
---|
301 | | - del_timer_sync(&tbl->proxy_timer); |
---|
302 | | - pneigh_queue_purge(&tbl->proxy_queue); |
---|
| 382 | +int neigh_carrier_down(struct neigh_table *tbl, struct net_device *dev) |
---|
| 383 | +{ |
---|
| 384 | + __neigh_ifdown(tbl, dev, true); |
---|
| 385 | + return 0; |
---|
| 386 | +} |
---|
| 387 | +EXPORT_SYMBOL(neigh_carrier_down); |
---|
| 388 | + |
---|
| 389 | +int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev) |
---|
| 390 | +{ |
---|
| 391 | + __neigh_ifdown(tbl, dev, false); |
---|
303 | 392 | return 0; |
---|
304 | 393 | } |
---|
305 | 394 | EXPORT_SYMBOL(neigh_ifdown); |
---|
306 | 395 | |
---|
307 | | -static struct neighbour *neigh_alloc(struct neigh_table *tbl, struct net_device *dev) |
---|
| 396 | +static struct neighbour *neigh_alloc(struct neigh_table *tbl, |
---|
| 397 | + struct net_device *dev, |
---|
| 398 | + u8 flags, bool exempt_from_gc) |
---|
308 | 399 | { |
---|
309 | 400 | struct neighbour *n = NULL; |
---|
310 | 401 | unsigned long now = jiffies; |
---|
311 | 402 | int entries; |
---|
312 | 403 | |
---|
313 | | - entries = atomic_inc_return(&tbl->entries) - 1; |
---|
| 404 | + if (exempt_from_gc) |
---|
| 405 | + goto do_alloc; |
---|
| 406 | + |
---|
| 407 | + entries = atomic_inc_return(&tbl->gc_entries) - 1; |
---|
314 | 408 | if (entries >= tbl->gc_thresh3 || |
---|
315 | 409 | (entries >= tbl->gc_thresh2 && |
---|
316 | 410 | time_after(now, tbl->last_flush + 5 * HZ))) { |
---|
.. | .. |
---|
323 | 417 | } |
---|
324 | 418 | } |
---|
325 | 419 | |
---|
| 420 | +do_alloc: |
---|
326 | 421 | n = kzalloc(tbl->entry_size + dev->neigh_priv_len, GFP_ATOMIC); |
---|
327 | 422 | if (!n) |
---|
328 | 423 | goto out_entries; |
---|
.. | .. |
---|
333 | 428 | n->updated = n->used = now; |
---|
334 | 429 | n->nud_state = NUD_NONE; |
---|
335 | 430 | n->output = neigh_blackhole; |
---|
| 431 | + n->flags = flags; |
---|
336 | 432 | seqlock_init(&n->hh.hh_lock); |
---|
337 | 433 | n->parms = neigh_parms_clone(&tbl->parms); |
---|
338 | 434 | timer_setup(&n->timer, neigh_timer_handler, 0); |
---|
.. | .. |
---|
341 | 437 | n->tbl = tbl; |
---|
342 | 438 | refcount_set(&n->refcnt, 1); |
---|
343 | 439 | n->dead = 1; |
---|
| 440 | + INIT_LIST_HEAD(&n->gc_list); |
---|
| 441 | + |
---|
| 442 | + atomic_inc(&tbl->entries); |
---|
344 | 443 | out: |
---|
345 | 444 | return n; |
---|
346 | 445 | |
---|
347 | 446 | out_entries: |
---|
348 | | - atomic_dec(&tbl->entries); |
---|
| 447 | + if (!exempt_from_gc) |
---|
| 448 | + atomic_dec(&tbl->gc_entries); |
---|
349 | 449 | goto out; |
---|
350 | 450 | } |
---|
351 | 451 | |
---|
.. | .. |
---|
492 | 592 | } |
---|
493 | 593 | EXPORT_SYMBOL(neigh_lookup_nodev); |
---|
494 | 594 | |
---|
495 | | -struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey, |
---|
496 | | - struct net_device *dev, bool want_ref) |
---|
| 595 | +static struct neighbour * |
---|
| 596 | +___neigh_create(struct neigh_table *tbl, const void *pkey, |
---|
| 597 | + struct net_device *dev, u8 flags, |
---|
| 598 | + bool exempt_from_gc, bool want_ref) |
---|
497 | 599 | { |
---|
498 | | - u32 hash_val; |
---|
499 | | - unsigned int key_len = tbl->key_len; |
---|
500 | | - int error; |
---|
501 | | - struct neighbour *n1, *rc, *n = neigh_alloc(tbl, dev); |
---|
| 600 | + u32 hash_val, key_len = tbl->key_len; |
---|
| 601 | + struct neighbour *n1, *rc, *n; |
---|
502 | 602 | struct neigh_hash_table *nht; |
---|
| 603 | + int error; |
---|
503 | 604 | |
---|
| 605 | + n = neigh_alloc(tbl, dev, flags, exempt_from_gc); |
---|
| 606 | + trace_neigh_create(tbl, dev, pkey, n, exempt_from_gc); |
---|
504 | 607 | if (!n) { |
---|
505 | 608 | rc = ERR_PTR(-ENOBUFS); |
---|
506 | 609 | goto out; |
---|
.. | .. |
---|
561 | 664 | } |
---|
562 | 665 | |
---|
563 | 666 | n->dead = 0; |
---|
| 667 | + if (!exempt_from_gc) |
---|
| 668 | + list_add_tail(&n->gc_list, &n->tbl->gc_list); |
---|
| 669 | + |
---|
564 | 670 | if (want_ref) |
---|
565 | 671 | neigh_hold(n); |
---|
566 | 672 | rcu_assign_pointer(n->next, |
---|
.. | .. |
---|
575 | 681 | out_tbl_unlock: |
---|
576 | 682 | write_unlock_bh(&tbl->lock); |
---|
577 | 683 | out_neigh_release: |
---|
| 684 | + if (!exempt_from_gc) |
---|
| 685 | + atomic_dec(&tbl->gc_entries); |
---|
578 | 686 | neigh_release(n); |
---|
579 | 687 | goto out; |
---|
| 688 | +} |
---|
| 689 | + |
---|
| 690 | +struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey, |
---|
| 691 | + struct net_device *dev, bool want_ref) |
---|
| 692 | +{ |
---|
| 693 | + return ___neigh_create(tbl, pkey, dev, 0, false, want_ref); |
---|
580 | 694 | } |
---|
581 | 695 | EXPORT_SYMBOL(__neigh_create); |
---|
582 | 696 | |
---|
.. | .. |
---|
841 | 955 | (state == NUD_FAILED || |
---|
842 | 956 | time_after(jiffies, n->used + NEIGH_VAR(n->parms, GC_STALETIME)))) { |
---|
843 | 957 | *np = n->next; |
---|
844 | | - n->dead = 1; |
---|
| 958 | + neigh_mark_dead(n); |
---|
845 | 959 | write_unlock(&n->lock); |
---|
846 | 960 | neigh_cleanup_and_release(n); |
---|
847 | 961 | continue; |
---|
.. | .. |
---|
915 | 1029 | if (neigh->ops->solicit) |
---|
916 | 1030 | neigh->ops->solicit(neigh, skb); |
---|
917 | 1031 | atomic_inc(&neigh->probes); |
---|
918 | | - kfree_skb(skb); |
---|
| 1032 | + consume_skb(skb); |
---|
919 | 1033 | } |
---|
920 | 1034 | |
---|
921 | 1035 | /* Called when a timer expires for a neighbour entry. */ |
---|
.. | .. |
---|
972 | 1086 | neigh->updated = jiffies; |
---|
973 | 1087 | atomic_set(&neigh->probes, 0); |
---|
974 | 1088 | notify = 1; |
---|
975 | | - next = now + NEIGH_VAR(neigh->parms, RETRANS_TIME); |
---|
| 1089 | + next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), |
---|
| 1090 | + HZ/100); |
---|
976 | 1091 | } |
---|
977 | 1092 | } else { |
---|
978 | 1093 | /* NUD_PROBE|NUD_INCOMPLETE */ |
---|
979 | | - next = now + NEIGH_VAR(neigh->parms, RETRANS_TIME); |
---|
| 1094 | + next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), HZ/100); |
---|
980 | 1095 | } |
---|
981 | 1096 | |
---|
982 | 1097 | if ((neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) && |
---|
.. | .. |
---|
988 | 1103 | } |
---|
989 | 1104 | |
---|
990 | 1105 | if (neigh->nud_state & NUD_IN_TIMER) { |
---|
991 | | - if (time_before(next, jiffies + HZ/2)) |
---|
992 | | - next = jiffies + HZ/2; |
---|
| 1106 | + if (time_before(next, jiffies + HZ/100)) |
---|
| 1107 | + next = jiffies + HZ/100; |
---|
993 | 1108 | if (!mod_timer(&neigh->timer, next)) |
---|
994 | 1109 | neigh_hold(neigh); |
---|
995 | 1110 | } |
---|
.. | .. |
---|
1002 | 1117 | |
---|
1003 | 1118 | if (notify) |
---|
1004 | 1119 | neigh_update_notify(neigh, 0); |
---|
| 1120 | + |
---|
| 1121 | + trace_neigh_timer_handler(neigh, 0); |
---|
1005 | 1122 | |
---|
1006 | 1123 | neigh_release(neigh); |
---|
1007 | 1124 | } |
---|
.. | .. |
---|
1030 | 1147 | neigh->nud_state = NUD_INCOMPLETE; |
---|
1031 | 1148 | neigh->updated = now; |
---|
1032 | 1149 | next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), |
---|
1033 | | - HZ/2); |
---|
| 1150 | + HZ/100); |
---|
1034 | 1151 | neigh_add_timer(neigh, next); |
---|
1035 | 1152 | immediate_probe = true; |
---|
1036 | 1153 | } else { |
---|
.. | .. |
---|
1075 | 1192 | else |
---|
1076 | 1193 | write_unlock(&neigh->lock); |
---|
1077 | 1194 | local_bh_enable(); |
---|
| 1195 | + trace_neigh_event_send_done(neigh, rc); |
---|
1078 | 1196 | return rc; |
---|
1079 | 1197 | |
---|
1080 | 1198 | out_dead: |
---|
.. | .. |
---|
1082 | 1200 | goto out_unlock_bh; |
---|
1083 | 1201 | write_unlock_bh(&neigh->lock); |
---|
1084 | 1202 | kfree_skb(skb); |
---|
| 1203 | + trace_neigh_event_send_dead(neigh, 1); |
---|
1085 | 1204 | return 1; |
---|
1086 | 1205 | } |
---|
1087 | 1206 | EXPORT_SYMBOL(__neigh_event_send); |
---|
.. | .. |
---|
1117 | 1236 | lladdr instead of overriding it |
---|
1118 | 1237 | if it is different. |
---|
1119 | 1238 | NEIGH_UPDATE_F_ADMIN means that the change is administrative. |
---|
1120 | | - |
---|
| 1239 | + NEIGH_UPDATE_F_USE means that the entry is user triggered. |
---|
1121 | 1240 | NEIGH_UPDATE_F_OVERRIDE_ISROUTER allows to override existing |
---|
1122 | 1241 | NTF_ROUTER flag. |
---|
1123 | 1242 | NEIGH_UPDATE_F_ISROUTER indicates if the neighbour is known as |
---|
.. | .. |
---|
1126 | 1245 | Caller MUST hold reference count on the entry. |
---|
1127 | 1246 | */ |
---|
1128 | 1247 | |
---|
1129 | | -int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, |
---|
1130 | | - u32 flags, u32 nlmsg_pid) |
---|
| 1248 | +static int __neigh_update(struct neighbour *neigh, const u8 *lladdr, |
---|
| 1249 | + u8 new, u32 flags, u32 nlmsg_pid, |
---|
| 1250 | + struct netlink_ext_ack *extack) |
---|
1131 | 1251 | { |
---|
| 1252 | + bool ext_learn_change = false; |
---|
1132 | 1253 | u8 old; |
---|
1133 | 1254 | int err; |
---|
1134 | 1255 | int notify = 0; |
---|
1135 | 1256 | struct net_device *dev; |
---|
1136 | 1257 | int update_isrouter = 0; |
---|
| 1258 | + |
---|
| 1259 | + trace_neigh_update(neigh, lladdr, new, flags, nlmsg_pid); |
---|
1137 | 1260 | |
---|
1138 | 1261 | write_lock_bh(&neigh->lock); |
---|
1139 | 1262 | |
---|
.. | .. |
---|
1141 | 1264 | old = neigh->nud_state; |
---|
1142 | 1265 | err = -EPERM; |
---|
1143 | 1266 | |
---|
| 1267 | + if (neigh->dead) { |
---|
| 1268 | + NL_SET_ERR_MSG(extack, "Neighbor entry is now dead"); |
---|
| 1269 | + new = old; |
---|
| 1270 | + goto out; |
---|
| 1271 | + } |
---|
1144 | 1272 | if (!(flags & NEIGH_UPDATE_F_ADMIN) && |
---|
1145 | 1273 | (old & (NUD_NOARP | NUD_PERMANENT))) |
---|
1146 | 1274 | goto out; |
---|
1147 | | - if (neigh->dead) |
---|
1148 | | - goto out; |
---|
1149 | 1275 | |
---|
1150 | | - neigh_update_ext_learned(neigh, flags, ¬ify); |
---|
| 1276 | + ext_learn_change = neigh_update_ext_learned(neigh, flags, ¬ify); |
---|
| 1277 | + if (flags & NEIGH_UPDATE_F_USE) { |
---|
| 1278 | + new = old & ~NUD_PERMANENT; |
---|
| 1279 | + neigh->nud_state = new; |
---|
| 1280 | + err = 0; |
---|
| 1281 | + goto out; |
---|
| 1282 | + } |
---|
1151 | 1283 | |
---|
1152 | 1284 | if (!(new & NUD_VALID)) { |
---|
1153 | 1285 | neigh_del_timer(neigh); |
---|
.. | .. |
---|
1182 | 1314 | use it, otherwise discard the request. |
---|
1183 | 1315 | */ |
---|
1184 | 1316 | err = -EINVAL; |
---|
1185 | | - if (!(old & NUD_VALID)) |
---|
| 1317 | + if (!(old & NUD_VALID)) { |
---|
| 1318 | + NL_SET_ERR_MSG(extack, "No link layer address given"); |
---|
1186 | 1319 | goto out; |
---|
| 1320 | + } |
---|
1187 | 1321 | lladdr = neigh->ha; |
---|
1188 | 1322 | } |
---|
1189 | 1323 | |
---|
.. | .. |
---|
1287 | 1421 | neigh->arp_queue_len_bytes = 0; |
---|
1288 | 1422 | } |
---|
1289 | 1423 | out: |
---|
1290 | | - if (update_isrouter) { |
---|
1291 | | - neigh->flags = (flags & NEIGH_UPDATE_F_ISROUTER) ? |
---|
1292 | | - (neigh->flags | NTF_ROUTER) : |
---|
1293 | | - (neigh->flags & ~NTF_ROUTER); |
---|
1294 | | - } |
---|
| 1424 | + if (update_isrouter) |
---|
| 1425 | + neigh_update_is_router(neigh, flags, ¬ify); |
---|
1295 | 1426 | write_unlock_bh(&neigh->lock); |
---|
| 1427 | + |
---|
| 1428 | + if (((new ^ old) & NUD_PERMANENT) || ext_learn_change) |
---|
| 1429 | + neigh_update_gc_list(neigh); |
---|
1296 | 1430 | |
---|
1297 | 1431 | if (notify) |
---|
1298 | 1432 | neigh_update_notify(neigh, nlmsg_pid); |
---|
1299 | 1433 | |
---|
| 1434 | + trace_neigh_update_done(neigh, err); |
---|
| 1435 | + |
---|
1300 | 1436 | return err; |
---|
| 1437 | +} |
---|
| 1438 | + |
---|
| 1439 | +int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, |
---|
| 1440 | + u32 flags, u32 nlmsg_pid) |
---|
| 1441 | +{ |
---|
| 1442 | + return __neigh_update(neigh, lladdr, new, flags, nlmsg_pid, NULL); |
---|
1301 | 1443 | } |
---|
1302 | 1444 | EXPORT_SYMBOL(neigh_update); |
---|
1303 | 1445 | |
---|
.. | .. |
---|
1314 | 1456 | neigh->nud_state = NUD_INCOMPLETE; |
---|
1315 | 1457 | atomic_set(&neigh->probes, neigh_max_probes(neigh)); |
---|
1316 | 1458 | neigh_add_timer(neigh, |
---|
1317 | | - jiffies + NEIGH_VAR(neigh->parms, RETRANS_TIME)); |
---|
| 1459 | + jiffies + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), |
---|
| 1460 | + HZ/100)); |
---|
1318 | 1461 | } |
---|
1319 | 1462 | EXPORT_SYMBOL(__neigh_set_probe_once); |
---|
1320 | 1463 | |
---|
.. | .. |
---|
1563 | 1706 | unsigned long phsize; |
---|
1564 | 1707 | |
---|
1565 | 1708 | INIT_LIST_HEAD(&tbl->parms_list); |
---|
| 1709 | + INIT_LIST_HEAD(&tbl->gc_list); |
---|
1566 | 1710 | list_add(&tbl->parms.list, &tbl->parms_list); |
---|
1567 | 1711 | write_pnet(&tbl->parms.net, &init_net); |
---|
1568 | 1712 | refcount_set(&tbl->parms.refcnt, 1); |
---|
.. | .. |
---|
1614 | 1758 | /* It is not clean... Fix it to unload IPv6 module safely */ |
---|
1615 | 1759 | cancel_delayed_work_sync(&tbl->gc_work); |
---|
1616 | 1760 | del_timer_sync(&tbl->proxy_timer); |
---|
1617 | | - pneigh_queue_purge(&tbl->proxy_queue); |
---|
| 1761 | + pneigh_queue_purge(&tbl->proxy_queue, NULL); |
---|
1618 | 1762 | neigh_ifdown(tbl, NULL); |
---|
1619 | 1763 | if (atomic_read(&tbl->entries)) |
---|
1620 | 1764 | pr_crit("neighbour leakage\n"); |
---|
.. | .. |
---|
1654 | 1798 | return tbl; |
---|
1655 | 1799 | } |
---|
1656 | 1800 | |
---|
| 1801 | +const struct nla_policy nda_policy[NDA_MAX+1] = { |
---|
| 1802 | + [NDA_UNSPEC] = { .strict_start_type = NDA_NH_ID }, |
---|
| 1803 | + [NDA_DST] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, |
---|
| 1804 | + [NDA_LLADDR] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, |
---|
| 1805 | + [NDA_CACHEINFO] = { .len = sizeof(struct nda_cacheinfo) }, |
---|
| 1806 | + [NDA_PROBES] = { .type = NLA_U32 }, |
---|
| 1807 | + [NDA_VLAN] = { .type = NLA_U16 }, |
---|
| 1808 | + [NDA_PORT] = { .type = NLA_U16 }, |
---|
| 1809 | + [NDA_VNI] = { .type = NLA_U32 }, |
---|
| 1810 | + [NDA_IFINDEX] = { .type = NLA_U32 }, |
---|
| 1811 | + [NDA_MASTER] = { .type = NLA_U32 }, |
---|
| 1812 | + [NDA_PROTOCOL] = { .type = NLA_U8 }, |
---|
| 1813 | + [NDA_NH_ID] = { .type = NLA_U32 }, |
---|
| 1814 | + [NDA_FDB_EXT_ATTRS] = { .type = NLA_NESTED }, |
---|
| 1815 | +}; |
---|
| 1816 | + |
---|
1657 | 1817 | static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, |
---|
1658 | 1818 | struct netlink_ext_ack *extack) |
---|
1659 | 1819 | { |
---|
.. | .. |
---|
1670 | 1830 | goto out; |
---|
1671 | 1831 | |
---|
1672 | 1832 | dst_attr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_DST); |
---|
1673 | | - if (dst_attr == NULL) |
---|
| 1833 | + if (!dst_attr) { |
---|
| 1834 | + NL_SET_ERR_MSG(extack, "Network address not specified"); |
---|
1674 | 1835 | goto out; |
---|
| 1836 | + } |
---|
1675 | 1837 | |
---|
1676 | 1838 | ndm = nlmsg_data(nlh); |
---|
1677 | 1839 | if (ndm->ndm_ifindex) { |
---|
.. | .. |
---|
1686 | 1848 | if (tbl == NULL) |
---|
1687 | 1849 | return -EAFNOSUPPORT; |
---|
1688 | 1850 | |
---|
1689 | | - if (nla_len(dst_attr) < (int)tbl->key_len) |
---|
| 1851 | + if (nla_len(dst_attr) < (int)tbl->key_len) { |
---|
| 1852 | + NL_SET_ERR_MSG(extack, "Invalid network address"); |
---|
1690 | 1853 | goto out; |
---|
| 1854 | + } |
---|
1691 | 1855 | |
---|
1692 | 1856 | if (ndm->ndm_flags & NTF_PROXY) { |
---|
1693 | 1857 | err = pneigh_delete(tbl, net, nla_data(dst_attr), dev); |
---|
.. | .. |
---|
1703 | 1867 | goto out; |
---|
1704 | 1868 | } |
---|
1705 | 1869 | |
---|
1706 | | - err = neigh_update(neigh, NULL, NUD_FAILED, |
---|
1707 | | - NEIGH_UPDATE_F_OVERRIDE | |
---|
1708 | | - NEIGH_UPDATE_F_ADMIN, |
---|
1709 | | - NETLINK_CB(skb).portid); |
---|
| 1870 | + err = __neigh_update(neigh, NULL, NUD_FAILED, |
---|
| 1871 | + NEIGH_UPDATE_F_OVERRIDE | NEIGH_UPDATE_F_ADMIN, |
---|
| 1872 | + NETLINK_CB(skb).portid, extack); |
---|
1710 | 1873 | write_lock_bh(&tbl->lock); |
---|
1711 | 1874 | neigh_release(neigh); |
---|
1712 | 1875 | neigh_remove_one(neigh, tbl); |
---|
.. | .. |
---|
1719 | 1882 | static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, |
---|
1720 | 1883 | struct netlink_ext_ack *extack) |
---|
1721 | 1884 | { |
---|
1722 | | - int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE; |
---|
| 1885 | + int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE | |
---|
| 1886 | + NEIGH_UPDATE_F_OVERRIDE_ISROUTER; |
---|
1723 | 1887 | struct net *net = sock_net(skb->sk); |
---|
1724 | 1888 | struct ndmsg *ndm; |
---|
1725 | 1889 | struct nlattr *tb[NDA_MAX+1]; |
---|
.. | .. |
---|
1727 | 1891 | struct net_device *dev = NULL; |
---|
1728 | 1892 | struct neighbour *neigh; |
---|
1729 | 1893 | void *dst, *lladdr; |
---|
| 1894 | + u8 protocol = 0; |
---|
1730 | 1895 | int err; |
---|
1731 | 1896 | |
---|
1732 | 1897 | ASSERT_RTNL(); |
---|
1733 | | - err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL, extack); |
---|
| 1898 | + err = nlmsg_parse_deprecated(nlh, sizeof(*ndm), tb, NDA_MAX, |
---|
| 1899 | + nda_policy, extack); |
---|
1734 | 1900 | if (err < 0) |
---|
1735 | 1901 | goto out; |
---|
1736 | 1902 | |
---|
1737 | 1903 | err = -EINVAL; |
---|
1738 | | - if (tb[NDA_DST] == NULL) |
---|
| 1904 | + if (!tb[NDA_DST]) { |
---|
| 1905 | + NL_SET_ERR_MSG(extack, "Network address not specified"); |
---|
1739 | 1906 | goto out; |
---|
| 1907 | + } |
---|
1740 | 1908 | |
---|
1741 | 1909 | ndm = nlmsg_data(nlh); |
---|
1742 | 1910 | if (ndm->ndm_ifindex) { |
---|
.. | .. |
---|
1746 | 1914 | goto out; |
---|
1747 | 1915 | } |
---|
1748 | 1916 | |
---|
1749 | | - if (tb[NDA_LLADDR] && nla_len(tb[NDA_LLADDR]) < dev->addr_len) |
---|
| 1917 | + if (tb[NDA_LLADDR] && nla_len(tb[NDA_LLADDR]) < dev->addr_len) { |
---|
| 1918 | + NL_SET_ERR_MSG(extack, "Invalid link address"); |
---|
1750 | 1919 | goto out; |
---|
| 1920 | + } |
---|
1751 | 1921 | } |
---|
1752 | 1922 | |
---|
1753 | 1923 | tbl = neigh_find_table(ndm->ndm_family); |
---|
1754 | 1924 | if (tbl == NULL) |
---|
1755 | 1925 | return -EAFNOSUPPORT; |
---|
1756 | 1926 | |
---|
1757 | | - if (nla_len(tb[NDA_DST]) < (int)tbl->key_len) |
---|
| 1927 | + if (nla_len(tb[NDA_DST]) < (int)tbl->key_len) { |
---|
| 1928 | + NL_SET_ERR_MSG(extack, "Invalid network address"); |
---|
1758 | 1929 | goto out; |
---|
| 1930 | + } |
---|
| 1931 | + |
---|
1759 | 1932 | dst = nla_data(tb[NDA_DST]); |
---|
1760 | 1933 | lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL; |
---|
| 1934 | + |
---|
| 1935 | + if (tb[NDA_PROTOCOL]) |
---|
| 1936 | + protocol = nla_get_u8(tb[NDA_PROTOCOL]); |
---|
1761 | 1937 | |
---|
1762 | 1938 | if (ndm->ndm_flags & NTF_PROXY) { |
---|
1763 | 1939 | struct pneigh_entry *pn; |
---|
.. | .. |
---|
1766 | 1942 | pn = pneigh_lookup(tbl, net, dst, dev, 1); |
---|
1767 | 1943 | if (pn) { |
---|
1768 | 1944 | pn->flags = ndm->ndm_flags; |
---|
| 1945 | + if (protocol) |
---|
| 1946 | + pn->protocol = protocol; |
---|
1769 | 1947 | err = 0; |
---|
1770 | 1948 | } |
---|
1771 | 1949 | goto out; |
---|
1772 | 1950 | } |
---|
1773 | 1951 | |
---|
1774 | | - if (dev == NULL) |
---|
| 1952 | + if (!dev) { |
---|
| 1953 | + NL_SET_ERR_MSG(extack, "Device not specified"); |
---|
1775 | 1954 | goto out; |
---|
| 1955 | + } |
---|
| 1956 | + |
---|
| 1957 | + if (tbl->allow_add && !tbl->allow_add(dev, extack)) { |
---|
| 1958 | + err = -EINVAL; |
---|
| 1959 | + goto out; |
---|
| 1960 | + } |
---|
1776 | 1961 | |
---|
1777 | 1962 | neigh = neigh_lookup(tbl, dst, dev); |
---|
1778 | 1963 | if (neigh == NULL) { |
---|
| 1964 | + bool exempt_from_gc; |
---|
| 1965 | + |
---|
1779 | 1966 | if (!(nlh->nlmsg_flags & NLM_F_CREATE)) { |
---|
1780 | 1967 | err = -ENOENT; |
---|
1781 | 1968 | goto out; |
---|
1782 | 1969 | } |
---|
1783 | 1970 | |
---|
1784 | | - neigh = __neigh_lookup_errno(tbl, dst, dev); |
---|
| 1971 | + exempt_from_gc = ndm->ndm_state & NUD_PERMANENT || |
---|
| 1972 | + ndm->ndm_flags & NTF_EXT_LEARNED; |
---|
| 1973 | + neigh = ___neigh_create(tbl, dst, dev, |
---|
| 1974 | + ndm->ndm_flags & NTF_EXT_LEARNED, |
---|
| 1975 | + exempt_from_gc, true); |
---|
1785 | 1976 | if (IS_ERR(neigh)) { |
---|
1786 | 1977 | err = PTR_ERR(neigh); |
---|
1787 | 1978 | goto out; |
---|
.. | .. |
---|
1794 | 1985 | } |
---|
1795 | 1986 | |
---|
1796 | 1987 | if (!(nlh->nlmsg_flags & NLM_F_REPLACE)) |
---|
1797 | | - flags &= ~NEIGH_UPDATE_F_OVERRIDE; |
---|
| 1988 | + flags &= ~(NEIGH_UPDATE_F_OVERRIDE | |
---|
| 1989 | + NEIGH_UPDATE_F_OVERRIDE_ISROUTER); |
---|
1798 | 1990 | } |
---|
1799 | 1991 | |
---|
| 1992 | + if (protocol) |
---|
| 1993 | + neigh->protocol = protocol; |
---|
1800 | 1994 | if (ndm->ndm_flags & NTF_EXT_LEARNED) |
---|
1801 | 1995 | flags |= NEIGH_UPDATE_F_EXT_LEARNED; |
---|
| 1996 | + if (ndm->ndm_flags & NTF_ROUTER) |
---|
| 1997 | + flags |= NEIGH_UPDATE_F_ISROUTER; |
---|
| 1998 | + if (ndm->ndm_flags & NTF_USE) |
---|
| 1999 | + flags |= NEIGH_UPDATE_F_USE; |
---|
1802 | 2000 | |
---|
1803 | | - if (ndm->ndm_flags & NTF_USE) { |
---|
| 2001 | + err = __neigh_update(neigh, lladdr, ndm->ndm_state, flags, |
---|
| 2002 | + NETLINK_CB(skb).portid, extack); |
---|
| 2003 | + if (!err && ndm->ndm_flags & NTF_USE) { |
---|
1804 | 2004 | neigh_event_send(neigh, NULL); |
---|
1805 | 2005 | err = 0; |
---|
1806 | | - } else |
---|
1807 | | - err = neigh_update(neigh, lladdr, ndm->ndm_state, flags, |
---|
1808 | | - NETLINK_CB(skb).portid); |
---|
| 2006 | + } |
---|
1809 | 2007 | neigh_release(neigh); |
---|
1810 | | - |
---|
1811 | 2008 | out: |
---|
1812 | 2009 | return err; |
---|
1813 | 2010 | } |
---|
.. | .. |
---|
1816 | 2013 | { |
---|
1817 | 2014 | struct nlattr *nest; |
---|
1818 | 2015 | |
---|
1819 | | - nest = nla_nest_start(skb, NDTA_PARMS); |
---|
| 2016 | + nest = nla_nest_start_noflag(skb, NDTA_PARMS); |
---|
1820 | 2017 | if (nest == NULL) |
---|
1821 | 2018 | return -ENOBUFS; |
---|
1822 | 2019 | |
---|
.. | .. |
---|
2018 | 2215 | bool found = false; |
---|
2019 | 2216 | int err, tidx; |
---|
2020 | 2217 | |
---|
2021 | | - err = nlmsg_parse(nlh, sizeof(*ndtmsg), tb, NDTA_MAX, |
---|
2022 | | - nl_neightbl_policy, extack); |
---|
| 2218 | + err = nlmsg_parse_deprecated(nlh, sizeof(*ndtmsg), tb, NDTA_MAX, |
---|
| 2219 | + nl_neightbl_policy, extack); |
---|
2023 | 2220 | if (err < 0) |
---|
2024 | 2221 | goto errout; |
---|
2025 | 2222 | |
---|
.. | .. |
---|
2056 | 2253 | struct neigh_parms *p; |
---|
2057 | 2254 | int i, ifindex = 0; |
---|
2058 | 2255 | |
---|
2059 | | - err = nla_parse_nested(tbp, NDTPA_MAX, tb[NDTA_PARMS], |
---|
2060 | | - nl_ntbl_parm_policy, extack); |
---|
| 2256 | + err = nla_parse_nested_deprecated(tbp, NDTPA_MAX, |
---|
| 2257 | + tb[NDTA_PARMS], |
---|
| 2258 | + nl_ntbl_parm_policy, extack); |
---|
2061 | 2259 | if (err < 0) |
---|
2062 | 2260 | goto errout_tbl_lock; |
---|
2063 | 2261 | |
---|
.. | .. |
---|
2169 | 2367 | return err; |
---|
2170 | 2368 | } |
---|
2171 | 2369 | |
---|
| 2370 | +static int neightbl_valid_dump_info(const struct nlmsghdr *nlh, |
---|
| 2371 | + struct netlink_ext_ack *extack) |
---|
| 2372 | +{ |
---|
| 2373 | + struct ndtmsg *ndtm; |
---|
| 2374 | + |
---|
| 2375 | + if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndtm))) { |
---|
| 2376 | + NL_SET_ERR_MSG(extack, "Invalid header for neighbor table dump request"); |
---|
| 2377 | + return -EINVAL; |
---|
| 2378 | + } |
---|
| 2379 | + |
---|
| 2380 | + ndtm = nlmsg_data(nlh); |
---|
| 2381 | + if (ndtm->ndtm_pad1 || ndtm->ndtm_pad2) { |
---|
| 2382 | + NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor table dump request"); |
---|
| 2383 | + return -EINVAL; |
---|
| 2384 | + } |
---|
| 2385 | + |
---|
| 2386 | + if (nlmsg_attrlen(nlh, sizeof(*ndtm))) { |
---|
| 2387 | + NL_SET_ERR_MSG(extack, "Invalid data after header in neighbor table dump request"); |
---|
| 2388 | + return -EINVAL; |
---|
| 2389 | + } |
---|
| 2390 | + |
---|
| 2391 | + return 0; |
---|
| 2392 | +} |
---|
| 2393 | + |
---|
2172 | 2394 | static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb) |
---|
2173 | 2395 | { |
---|
| 2396 | + const struct nlmsghdr *nlh = cb->nlh; |
---|
2174 | 2397 | struct net *net = sock_net(skb->sk); |
---|
2175 | 2398 | int family, tidx, nidx = 0; |
---|
2176 | 2399 | int tbl_skip = cb->args[0]; |
---|
2177 | 2400 | int neigh_skip = cb->args[1]; |
---|
2178 | 2401 | struct neigh_table *tbl; |
---|
2179 | 2402 | |
---|
2180 | | - family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family; |
---|
| 2403 | + if (cb->strict_check) { |
---|
| 2404 | + int err = neightbl_valid_dump_info(nlh, cb->extack); |
---|
| 2405 | + |
---|
| 2406 | + if (err < 0) |
---|
| 2407 | + return err; |
---|
| 2408 | + } |
---|
| 2409 | + |
---|
| 2410 | + family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family; |
---|
2181 | 2411 | |
---|
2182 | 2412 | for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) { |
---|
2183 | 2413 | struct neigh_parms *p; |
---|
.. | .. |
---|
2190 | 2420 | continue; |
---|
2191 | 2421 | |
---|
2192 | 2422 | if (neightbl_fill_info(skb, tbl, NETLINK_CB(cb->skb).portid, |
---|
2193 | | - cb->nlh->nlmsg_seq, RTM_NEWNEIGHTBL, |
---|
| 2423 | + nlh->nlmsg_seq, RTM_NEWNEIGHTBL, |
---|
2194 | 2424 | NLM_F_MULTI) < 0) |
---|
2195 | 2425 | break; |
---|
2196 | 2426 | |
---|
.. | .. |
---|
2205 | 2435 | |
---|
2206 | 2436 | if (neightbl_fill_param_info(skb, tbl, p, |
---|
2207 | 2437 | NETLINK_CB(cb->skb).portid, |
---|
2208 | | - cb->nlh->nlmsg_seq, |
---|
| 2438 | + nlh->nlmsg_seq, |
---|
2209 | 2439 | RTM_NEWNEIGHTBL, |
---|
2210 | 2440 | NLM_F_MULTI) < 0) |
---|
2211 | 2441 | goto out; |
---|
.. | .. |
---|
2267 | 2497 | nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci)) |
---|
2268 | 2498 | goto nla_put_failure; |
---|
2269 | 2499 | |
---|
| 2500 | + if (neigh->protocol && nla_put_u8(skb, NDA_PROTOCOL, neigh->protocol)) |
---|
| 2501 | + goto nla_put_failure; |
---|
| 2502 | + |
---|
2270 | 2503 | nlmsg_end(skb, nlh); |
---|
2271 | 2504 | return 0; |
---|
2272 | 2505 | |
---|
.. | .. |
---|
2298 | 2531 | if (nla_put(skb, NDA_DST, tbl->key_len, pn->key)) |
---|
2299 | 2532 | goto nla_put_failure; |
---|
2300 | 2533 | |
---|
| 2534 | + if (pn->protocol && nla_put_u8(skb, NDA_PROTOCOL, pn->protocol)) |
---|
| 2535 | + goto nla_put_failure; |
---|
| 2536 | + |
---|
2301 | 2537 | nlmsg_end(skb, nlh); |
---|
2302 | 2538 | return 0; |
---|
2303 | 2539 | |
---|
.. | .. |
---|
2319 | 2555 | if (!master_idx) |
---|
2320 | 2556 | return false; |
---|
2321 | 2557 | |
---|
2322 | | - master = netdev_master_upper_dev_get(dev); |
---|
| 2558 | + master = dev ? netdev_master_upper_dev_get(dev) : NULL; |
---|
2323 | 2559 | if (!master || master->ifindex != master_idx) |
---|
2324 | 2560 | return true; |
---|
2325 | 2561 | |
---|
.. | .. |
---|
2328 | 2564 | |
---|
2329 | 2565 | static bool neigh_ifindex_filtered(struct net_device *dev, int filter_idx) |
---|
2330 | 2566 | { |
---|
2331 | | - if (filter_idx && dev->ifindex != filter_idx) |
---|
| 2567 | + if (filter_idx && (!dev || dev->ifindex != filter_idx)) |
---|
2332 | 2568 | return true; |
---|
2333 | 2569 | |
---|
2334 | 2570 | return false; |
---|
2335 | 2571 | } |
---|
2336 | 2572 | |
---|
| 2573 | +struct neigh_dump_filter { |
---|
| 2574 | + int master_idx; |
---|
| 2575 | + int dev_idx; |
---|
| 2576 | +}; |
---|
| 2577 | + |
---|
2337 | 2578 | static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, |
---|
2338 | | - struct netlink_callback *cb) |
---|
| 2579 | + struct netlink_callback *cb, |
---|
| 2580 | + struct neigh_dump_filter *filter) |
---|
2339 | 2581 | { |
---|
2340 | 2582 | struct net *net = sock_net(skb->sk); |
---|
2341 | | - const struct nlmsghdr *nlh = cb->nlh; |
---|
2342 | | - struct nlattr *tb[NDA_MAX + 1]; |
---|
2343 | 2583 | struct neighbour *n; |
---|
2344 | 2584 | int rc, h, s_h = cb->args[1]; |
---|
2345 | 2585 | int idx, s_idx = idx = cb->args[2]; |
---|
2346 | 2586 | struct neigh_hash_table *nht; |
---|
2347 | | - int filter_master_idx = 0, filter_idx = 0; |
---|
2348 | 2587 | unsigned int flags = NLM_F_MULTI; |
---|
2349 | | - int err; |
---|
2350 | 2588 | |
---|
2351 | | - err = nlmsg_parse(nlh, sizeof(struct ndmsg), tb, NDA_MAX, NULL, NULL); |
---|
2352 | | - if (!err) { |
---|
2353 | | - if (tb[NDA_IFINDEX]) { |
---|
2354 | | - if (nla_len(tb[NDA_IFINDEX]) != sizeof(u32)) |
---|
2355 | | - return -EINVAL; |
---|
2356 | | - filter_idx = nla_get_u32(tb[NDA_IFINDEX]); |
---|
2357 | | - } |
---|
2358 | | - if (tb[NDA_MASTER]) { |
---|
2359 | | - if (nla_len(tb[NDA_MASTER]) != sizeof(u32)) |
---|
2360 | | - return -EINVAL; |
---|
2361 | | - filter_master_idx = nla_get_u32(tb[NDA_MASTER]); |
---|
2362 | | - } |
---|
2363 | | - if (filter_idx || filter_master_idx) |
---|
2364 | | - flags |= NLM_F_DUMP_FILTERED; |
---|
2365 | | - } |
---|
| 2589 | + if (filter->dev_idx || filter->master_idx) |
---|
| 2590 | + flags |= NLM_F_DUMP_FILTERED; |
---|
2366 | 2591 | |
---|
2367 | 2592 | rcu_read_lock_bh(); |
---|
2368 | 2593 | nht = rcu_dereference_bh(tbl->nht); |
---|
.. | .. |
---|
2375 | 2600 | n = rcu_dereference_bh(n->next)) { |
---|
2376 | 2601 | if (idx < s_idx || !net_eq(dev_net(n->dev), net)) |
---|
2377 | 2602 | goto next; |
---|
2378 | | - if (neigh_ifindex_filtered(n->dev, filter_idx) || |
---|
2379 | | - neigh_master_filtered(n->dev, filter_master_idx)) |
---|
| 2603 | + if (neigh_ifindex_filtered(n->dev, filter->dev_idx) || |
---|
| 2604 | + neigh_master_filtered(n->dev, filter->master_idx)) |
---|
2380 | 2605 | goto next; |
---|
2381 | 2606 | if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid, |
---|
2382 | 2607 | cb->nlh->nlmsg_seq, |
---|
.. | .. |
---|
2398 | 2623 | } |
---|
2399 | 2624 | |
---|
2400 | 2625 | static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, |
---|
2401 | | - struct netlink_callback *cb) |
---|
| 2626 | + struct netlink_callback *cb, |
---|
| 2627 | + struct neigh_dump_filter *filter) |
---|
2402 | 2628 | { |
---|
2403 | 2629 | struct pneigh_entry *n; |
---|
2404 | 2630 | struct net *net = sock_net(skb->sk); |
---|
2405 | 2631 | int rc, h, s_h = cb->args[3]; |
---|
2406 | 2632 | int idx, s_idx = idx = cb->args[4]; |
---|
| 2633 | + unsigned int flags = NLM_F_MULTI; |
---|
| 2634 | + |
---|
| 2635 | + if (filter->dev_idx || filter->master_idx) |
---|
| 2636 | + flags |= NLM_F_DUMP_FILTERED; |
---|
2407 | 2637 | |
---|
2408 | 2638 | read_lock_bh(&tbl->lock); |
---|
2409 | 2639 | |
---|
.. | .. |
---|
2413 | 2643 | for (n = tbl->phash_buckets[h], idx = 0; n; n = n->next) { |
---|
2414 | 2644 | if (idx < s_idx || pneigh_net(n) != net) |
---|
2415 | 2645 | goto next; |
---|
| 2646 | + if (neigh_ifindex_filtered(n->dev, filter->dev_idx) || |
---|
| 2647 | + neigh_master_filtered(n->dev, filter->master_idx)) |
---|
| 2648 | + goto next; |
---|
2416 | 2649 | if (pneigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid, |
---|
2417 | 2650 | cb->nlh->nlmsg_seq, |
---|
2418 | | - RTM_NEWNEIGH, |
---|
2419 | | - NLM_F_MULTI, tbl) < 0) { |
---|
| 2651 | + RTM_NEWNEIGH, flags, tbl) < 0) { |
---|
2420 | 2652 | read_unlock_bh(&tbl->lock); |
---|
2421 | 2653 | rc = -1; |
---|
2422 | 2654 | goto out; |
---|
.. | .. |
---|
2435 | 2667 | |
---|
2436 | 2668 | } |
---|
2437 | 2669 | |
---|
| 2670 | +static int neigh_valid_dump_req(const struct nlmsghdr *nlh, |
---|
| 2671 | + bool strict_check, |
---|
| 2672 | + struct neigh_dump_filter *filter, |
---|
| 2673 | + struct netlink_ext_ack *extack) |
---|
| 2674 | +{ |
---|
| 2675 | + struct nlattr *tb[NDA_MAX + 1]; |
---|
| 2676 | + int err, i; |
---|
| 2677 | + |
---|
| 2678 | + if (strict_check) { |
---|
| 2679 | + struct ndmsg *ndm; |
---|
| 2680 | + |
---|
| 2681 | + if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) { |
---|
| 2682 | + NL_SET_ERR_MSG(extack, "Invalid header for neighbor dump request"); |
---|
| 2683 | + return -EINVAL; |
---|
| 2684 | + } |
---|
| 2685 | + |
---|
| 2686 | + ndm = nlmsg_data(nlh); |
---|
| 2687 | + if (ndm->ndm_pad1 || ndm->ndm_pad2 || ndm->ndm_ifindex || |
---|
| 2688 | + ndm->ndm_state || ndm->ndm_type) { |
---|
| 2689 | + NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor dump request"); |
---|
| 2690 | + return -EINVAL; |
---|
| 2691 | + } |
---|
| 2692 | + |
---|
| 2693 | + if (ndm->ndm_flags & ~NTF_PROXY) { |
---|
| 2694 | + NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor dump request"); |
---|
| 2695 | + return -EINVAL; |
---|
| 2696 | + } |
---|
| 2697 | + |
---|
| 2698 | + err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct ndmsg), |
---|
| 2699 | + tb, NDA_MAX, nda_policy, |
---|
| 2700 | + extack); |
---|
| 2701 | + } else { |
---|
| 2702 | + err = nlmsg_parse_deprecated(nlh, sizeof(struct ndmsg), tb, |
---|
| 2703 | + NDA_MAX, nda_policy, extack); |
---|
| 2704 | + } |
---|
| 2705 | + if (err < 0) |
---|
| 2706 | + return err; |
---|
| 2707 | + |
---|
| 2708 | + for (i = 0; i <= NDA_MAX; ++i) { |
---|
| 2709 | + if (!tb[i]) |
---|
| 2710 | + continue; |
---|
| 2711 | + |
---|
| 2712 | + /* all new attributes should require strict_check */ |
---|
| 2713 | + switch (i) { |
---|
| 2714 | + case NDA_IFINDEX: |
---|
| 2715 | + filter->dev_idx = nla_get_u32(tb[i]); |
---|
| 2716 | + break; |
---|
| 2717 | + case NDA_MASTER: |
---|
| 2718 | + filter->master_idx = nla_get_u32(tb[i]); |
---|
| 2719 | + break; |
---|
| 2720 | + default: |
---|
| 2721 | + if (strict_check) { |
---|
| 2722 | + NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor dump request"); |
---|
| 2723 | + return -EINVAL; |
---|
| 2724 | + } |
---|
| 2725 | + } |
---|
| 2726 | + } |
---|
| 2727 | + |
---|
| 2728 | + return 0; |
---|
| 2729 | +} |
---|
| 2730 | + |
---|
2438 | 2731 | static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb) |
---|
2439 | 2732 | { |
---|
| 2733 | + const struct nlmsghdr *nlh = cb->nlh; |
---|
| 2734 | + struct neigh_dump_filter filter = {}; |
---|
2440 | 2735 | struct neigh_table *tbl; |
---|
2441 | 2736 | int t, family, s_t; |
---|
2442 | 2737 | int proxy = 0; |
---|
2443 | 2738 | int err; |
---|
2444 | 2739 | |
---|
2445 | | - family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family; |
---|
| 2740 | + family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family; |
---|
2446 | 2741 | |
---|
2447 | 2742 | /* check for full ndmsg structure presence, family member is |
---|
2448 | 2743 | * the same for both structures |
---|
2449 | 2744 | */ |
---|
2450 | | - if (nlmsg_len(cb->nlh) >= sizeof(struct ndmsg) && |
---|
2451 | | - ((struct ndmsg *) nlmsg_data(cb->nlh))->ndm_flags == NTF_PROXY) |
---|
| 2745 | + if (nlmsg_len(nlh) >= sizeof(struct ndmsg) && |
---|
| 2746 | + ((struct ndmsg *)nlmsg_data(nlh))->ndm_flags == NTF_PROXY) |
---|
2452 | 2747 | proxy = 1; |
---|
| 2748 | + |
---|
| 2749 | + err = neigh_valid_dump_req(nlh, cb->strict_check, &filter, cb->extack); |
---|
| 2750 | + if (err < 0 && cb->strict_check) |
---|
| 2751 | + return err; |
---|
2453 | 2752 | |
---|
2454 | 2753 | s_t = cb->args[0]; |
---|
2455 | 2754 | |
---|
.. | .. |
---|
2464 | 2763 | memset(&cb->args[1], 0, sizeof(cb->args) - |
---|
2465 | 2764 | sizeof(cb->args[0])); |
---|
2466 | 2765 | if (proxy) |
---|
2467 | | - err = pneigh_dump_table(tbl, skb, cb); |
---|
| 2766 | + err = pneigh_dump_table(tbl, skb, cb, &filter); |
---|
2468 | 2767 | else |
---|
2469 | | - err = neigh_dump_table(tbl, skb, cb); |
---|
| 2768 | + err = neigh_dump_table(tbl, skb, cb, &filter); |
---|
2470 | 2769 | if (err < 0) |
---|
2471 | 2770 | break; |
---|
2472 | 2771 | } |
---|
2473 | 2772 | |
---|
2474 | 2773 | cb->args[0] = t; |
---|
2475 | 2774 | return skb->len; |
---|
| 2775 | +} |
---|
| 2776 | + |
---|
| 2777 | +static int neigh_valid_get_req(const struct nlmsghdr *nlh, |
---|
| 2778 | + struct neigh_table **tbl, |
---|
| 2779 | + void **dst, int *dev_idx, u8 *ndm_flags, |
---|
| 2780 | + struct netlink_ext_ack *extack) |
---|
| 2781 | +{ |
---|
| 2782 | + struct nlattr *tb[NDA_MAX + 1]; |
---|
| 2783 | + struct ndmsg *ndm; |
---|
| 2784 | + int err, i; |
---|
| 2785 | + |
---|
| 2786 | + if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) { |
---|
| 2787 | + NL_SET_ERR_MSG(extack, "Invalid header for neighbor get request"); |
---|
| 2788 | + return -EINVAL; |
---|
| 2789 | + } |
---|
| 2790 | + |
---|
| 2791 | + ndm = nlmsg_data(nlh); |
---|
| 2792 | + if (ndm->ndm_pad1 || ndm->ndm_pad2 || ndm->ndm_state || |
---|
| 2793 | + ndm->ndm_type) { |
---|
| 2794 | + NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor get request"); |
---|
| 2795 | + return -EINVAL; |
---|
| 2796 | + } |
---|
| 2797 | + |
---|
| 2798 | + if (ndm->ndm_flags & ~NTF_PROXY) { |
---|
| 2799 | + NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor get request"); |
---|
| 2800 | + return -EINVAL; |
---|
| 2801 | + } |
---|
| 2802 | + |
---|
| 2803 | + err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct ndmsg), tb, |
---|
| 2804 | + NDA_MAX, nda_policy, extack); |
---|
| 2805 | + if (err < 0) |
---|
| 2806 | + return err; |
---|
| 2807 | + |
---|
| 2808 | + *ndm_flags = ndm->ndm_flags; |
---|
| 2809 | + *dev_idx = ndm->ndm_ifindex; |
---|
| 2810 | + *tbl = neigh_find_table(ndm->ndm_family); |
---|
| 2811 | + if (*tbl == NULL) { |
---|
| 2812 | + NL_SET_ERR_MSG(extack, "Unsupported family in header for neighbor get request"); |
---|
| 2813 | + return -EAFNOSUPPORT; |
---|
| 2814 | + } |
---|
| 2815 | + |
---|
| 2816 | + for (i = 0; i <= NDA_MAX; ++i) { |
---|
| 2817 | + if (!tb[i]) |
---|
| 2818 | + continue; |
---|
| 2819 | + |
---|
| 2820 | + switch (i) { |
---|
| 2821 | + case NDA_DST: |
---|
| 2822 | + if (nla_len(tb[i]) != (int)(*tbl)->key_len) { |
---|
| 2823 | + NL_SET_ERR_MSG(extack, "Invalid network address in neighbor get request"); |
---|
| 2824 | + return -EINVAL; |
---|
| 2825 | + } |
---|
| 2826 | + *dst = nla_data(tb[i]); |
---|
| 2827 | + break; |
---|
| 2828 | + default: |
---|
| 2829 | + NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor get request"); |
---|
| 2830 | + return -EINVAL; |
---|
| 2831 | + } |
---|
| 2832 | + } |
---|
| 2833 | + |
---|
| 2834 | + return 0; |
---|
| 2835 | +} |
---|
| 2836 | + |
---|
| 2837 | +static inline size_t neigh_nlmsg_size(void) |
---|
| 2838 | +{ |
---|
| 2839 | + return NLMSG_ALIGN(sizeof(struct ndmsg)) |
---|
| 2840 | + + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */ |
---|
| 2841 | + + nla_total_size(MAX_ADDR_LEN) /* NDA_LLADDR */ |
---|
| 2842 | + + nla_total_size(sizeof(struct nda_cacheinfo)) |
---|
| 2843 | + + nla_total_size(4) /* NDA_PROBES */ |
---|
| 2844 | + + nla_total_size(1); /* NDA_PROTOCOL */ |
---|
| 2845 | +} |
---|
| 2846 | + |
---|
| 2847 | +static int neigh_get_reply(struct net *net, struct neighbour *neigh, |
---|
| 2848 | + u32 pid, u32 seq) |
---|
| 2849 | +{ |
---|
| 2850 | + struct sk_buff *skb; |
---|
| 2851 | + int err = 0; |
---|
| 2852 | + |
---|
| 2853 | + skb = nlmsg_new(neigh_nlmsg_size(), GFP_KERNEL); |
---|
| 2854 | + if (!skb) |
---|
| 2855 | + return -ENOBUFS; |
---|
| 2856 | + |
---|
| 2857 | + err = neigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0); |
---|
| 2858 | + if (err) { |
---|
| 2859 | + kfree_skb(skb); |
---|
| 2860 | + goto errout; |
---|
| 2861 | + } |
---|
| 2862 | + |
---|
| 2863 | + err = rtnl_unicast(skb, net, pid); |
---|
| 2864 | +errout: |
---|
| 2865 | + return err; |
---|
| 2866 | +} |
---|
| 2867 | + |
---|
| 2868 | +static inline size_t pneigh_nlmsg_size(void) |
---|
| 2869 | +{ |
---|
| 2870 | + return NLMSG_ALIGN(sizeof(struct ndmsg)) |
---|
| 2871 | + + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */ |
---|
| 2872 | + + nla_total_size(1); /* NDA_PROTOCOL */ |
---|
| 2873 | +} |
---|
| 2874 | + |
---|
| 2875 | +static int pneigh_get_reply(struct net *net, struct pneigh_entry *neigh, |
---|
| 2876 | + u32 pid, u32 seq, struct neigh_table *tbl) |
---|
| 2877 | +{ |
---|
| 2878 | + struct sk_buff *skb; |
---|
| 2879 | + int err = 0; |
---|
| 2880 | + |
---|
| 2881 | + skb = nlmsg_new(pneigh_nlmsg_size(), GFP_KERNEL); |
---|
| 2882 | + if (!skb) |
---|
| 2883 | + return -ENOBUFS; |
---|
| 2884 | + |
---|
| 2885 | + err = pneigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0, tbl); |
---|
| 2886 | + if (err) { |
---|
| 2887 | + kfree_skb(skb); |
---|
| 2888 | + goto errout; |
---|
| 2889 | + } |
---|
| 2890 | + |
---|
| 2891 | + err = rtnl_unicast(skb, net, pid); |
---|
| 2892 | +errout: |
---|
| 2893 | + return err; |
---|
| 2894 | +} |
---|
| 2895 | + |
---|
| 2896 | +static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh, |
---|
| 2897 | + struct netlink_ext_ack *extack) |
---|
| 2898 | +{ |
---|
| 2899 | + struct net *net = sock_net(in_skb->sk); |
---|
| 2900 | + struct net_device *dev = NULL; |
---|
| 2901 | + struct neigh_table *tbl = NULL; |
---|
| 2902 | + struct neighbour *neigh; |
---|
| 2903 | + void *dst = NULL; |
---|
| 2904 | + u8 ndm_flags = 0; |
---|
| 2905 | + int dev_idx = 0; |
---|
| 2906 | + int err; |
---|
| 2907 | + |
---|
| 2908 | + err = neigh_valid_get_req(nlh, &tbl, &dst, &dev_idx, &ndm_flags, |
---|
| 2909 | + extack); |
---|
| 2910 | + if (err < 0) |
---|
| 2911 | + return err; |
---|
| 2912 | + |
---|
| 2913 | + if (dev_idx) { |
---|
| 2914 | + dev = __dev_get_by_index(net, dev_idx); |
---|
| 2915 | + if (!dev) { |
---|
| 2916 | + NL_SET_ERR_MSG(extack, "Unknown device ifindex"); |
---|
| 2917 | + return -ENODEV; |
---|
| 2918 | + } |
---|
| 2919 | + } |
---|
| 2920 | + |
---|
| 2921 | + if (!dst) { |
---|
| 2922 | + NL_SET_ERR_MSG(extack, "Network address not specified"); |
---|
| 2923 | + return -EINVAL; |
---|
| 2924 | + } |
---|
| 2925 | + |
---|
| 2926 | + if (ndm_flags & NTF_PROXY) { |
---|
| 2927 | + struct pneigh_entry *pn; |
---|
| 2928 | + |
---|
| 2929 | + pn = pneigh_lookup(tbl, net, dst, dev, 0); |
---|
| 2930 | + if (!pn) { |
---|
| 2931 | + NL_SET_ERR_MSG(extack, "Proxy neighbour entry not found"); |
---|
| 2932 | + return -ENOENT; |
---|
| 2933 | + } |
---|
| 2934 | + return pneigh_get_reply(net, pn, NETLINK_CB(in_skb).portid, |
---|
| 2935 | + nlh->nlmsg_seq, tbl); |
---|
| 2936 | + } |
---|
| 2937 | + |
---|
| 2938 | + if (!dev) { |
---|
| 2939 | + NL_SET_ERR_MSG(extack, "No device specified"); |
---|
| 2940 | + return -EINVAL; |
---|
| 2941 | + } |
---|
| 2942 | + |
---|
| 2943 | + neigh = neigh_lookup(tbl, dst, dev); |
---|
| 2944 | + if (!neigh) { |
---|
| 2945 | + NL_SET_ERR_MSG(extack, "Neighbour entry not found"); |
---|
| 2946 | + return -ENOENT; |
---|
| 2947 | + } |
---|
| 2948 | + |
---|
| 2949 | + err = neigh_get_reply(net, neigh, NETLINK_CB(in_skb).portid, |
---|
| 2950 | + nlh->nlmsg_seq); |
---|
| 2951 | + |
---|
| 2952 | + neigh_release(neigh); |
---|
| 2953 | + |
---|
| 2954 | + return err; |
---|
2476 | 2955 | } |
---|
2477 | 2956 | |
---|
2478 | 2957 | void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie) |
---|
.. | .. |
---|
2521 | 3000 | rcu_assign_pointer(*np, |
---|
2522 | 3001 | rcu_dereference_protected(n->next, |
---|
2523 | 3002 | lockdep_is_held(&tbl->lock))); |
---|
2524 | | - n->dead = 1; |
---|
| 3003 | + neigh_mark_dead(n); |
---|
2525 | 3004 | } else |
---|
2526 | 3005 | np = &n->next; |
---|
2527 | 3006 | write_unlock(&n->lock); |
---|
.. | .. |
---|
2584 | 3063 | struct net *net = seq_file_net(seq); |
---|
2585 | 3064 | struct neigh_hash_table *nht = state->nht; |
---|
2586 | 3065 | struct neighbour *n = NULL; |
---|
2587 | | - int bucket = state->bucket; |
---|
| 3066 | + int bucket; |
---|
2588 | 3067 | |
---|
2589 | 3068 | state->flags &= ~NEIGH_SEQ_IS_PNEIGH; |
---|
2590 | 3069 | for (bucket = 0; bucket < (1 << nht->hash_shift); bucket++) { |
---|
.. | .. |
---|
2896 | 3375 | }; |
---|
2897 | 3376 | #endif /* CONFIG_PROC_FS */ |
---|
2898 | 3377 | |
---|
2899 | | -static inline size_t neigh_nlmsg_size(void) |
---|
2900 | | -{ |
---|
2901 | | - return NLMSG_ALIGN(sizeof(struct ndmsg)) |
---|
2902 | | - + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */ |
---|
2903 | | - + nla_total_size(MAX_ADDR_LEN) /* NDA_LLADDR */ |
---|
2904 | | - + nla_total_size(sizeof(struct nda_cacheinfo)) |
---|
2905 | | - + nla_total_size(4); /* NDA_PROBES */ |
---|
2906 | | -} |
---|
2907 | | - |
---|
2908 | 3378 | static void __neigh_notify(struct neighbour *n, int type, int flags, |
---|
2909 | 3379 | u32 pid) |
---|
2910 | 3380 | { |
---|
.. | .. |
---|
2937 | 3407 | EXPORT_SYMBOL(neigh_app_ns); |
---|
2938 | 3408 | |
---|
2939 | 3409 | #ifdef CONFIG_SYSCTL |
---|
2940 | | -static int zero; |
---|
2941 | | -static int int_max = INT_MAX; |
---|
2942 | 3410 | static int unres_qlen_max = INT_MAX / SKB_TRUESIZE(ETH_FRAME_LEN); |
---|
2943 | 3411 | |
---|
2944 | 3412 | static int proc_unres_qlen(struct ctl_table *ctl, int write, |
---|
2945 | | - void __user *buffer, size_t *lenp, loff_t *ppos) |
---|
| 3413 | + void *buffer, size_t *lenp, loff_t *ppos) |
---|
2946 | 3414 | { |
---|
2947 | 3415 | int size, ret; |
---|
2948 | 3416 | struct ctl_table tmp = *ctl; |
---|
2949 | 3417 | |
---|
2950 | | - tmp.extra1 = &zero; |
---|
| 3418 | + tmp.extra1 = SYSCTL_ZERO; |
---|
2951 | 3419 | tmp.extra2 = &unres_qlen_max; |
---|
2952 | 3420 | tmp.data = &size; |
---|
2953 | 3421 | |
---|
.. | .. |
---|
3006 | 3474 | } |
---|
3007 | 3475 | |
---|
3008 | 3476 | static int neigh_proc_dointvec_zero_intmax(struct ctl_table *ctl, int write, |
---|
3009 | | - void __user *buffer, |
---|
3010 | | - size_t *lenp, loff_t *ppos) |
---|
| 3477 | + void *buffer, size_t *lenp, |
---|
| 3478 | + loff_t *ppos) |
---|
3011 | 3479 | { |
---|
3012 | 3480 | struct ctl_table tmp = *ctl; |
---|
3013 | 3481 | int ret; |
---|
3014 | 3482 | |
---|
3015 | | - tmp.extra1 = &zero; |
---|
3016 | | - tmp.extra2 = &int_max; |
---|
| 3483 | + tmp.extra1 = SYSCTL_ZERO; |
---|
| 3484 | + tmp.extra2 = SYSCTL_INT_MAX; |
---|
3017 | 3485 | |
---|
3018 | 3486 | ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); |
---|
3019 | 3487 | neigh_proc_update(ctl, write); |
---|
3020 | 3488 | return ret; |
---|
3021 | 3489 | } |
---|
3022 | 3490 | |
---|
3023 | | -int neigh_proc_dointvec(struct ctl_table *ctl, int write, |
---|
3024 | | - void __user *buffer, size_t *lenp, loff_t *ppos) |
---|
| 3491 | +int neigh_proc_dointvec(struct ctl_table *ctl, int write, void *buffer, |
---|
| 3492 | + size_t *lenp, loff_t *ppos) |
---|
3025 | 3493 | { |
---|
3026 | 3494 | int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); |
---|
3027 | 3495 | |
---|
.. | .. |
---|
3030 | 3498 | } |
---|
3031 | 3499 | EXPORT_SYMBOL(neigh_proc_dointvec); |
---|
3032 | 3500 | |
---|
3033 | | -int neigh_proc_dointvec_jiffies(struct ctl_table *ctl, int write, |
---|
3034 | | - void __user *buffer, |
---|
| 3501 | +int neigh_proc_dointvec_jiffies(struct ctl_table *ctl, int write, void *buffer, |
---|
3035 | 3502 | size_t *lenp, loff_t *ppos) |
---|
3036 | 3503 | { |
---|
3037 | 3504 | int ret = proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos); |
---|
.. | .. |
---|
3042 | 3509 | EXPORT_SYMBOL(neigh_proc_dointvec_jiffies); |
---|
3043 | 3510 | |
---|
3044 | 3511 | static int neigh_proc_dointvec_userhz_jiffies(struct ctl_table *ctl, int write, |
---|
3045 | | - void __user *buffer, |
---|
3046 | | - size_t *lenp, loff_t *ppos) |
---|
| 3512 | + void *buffer, size_t *lenp, |
---|
| 3513 | + loff_t *ppos) |
---|
3047 | 3514 | { |
---|
3048 | 3515 | int ret = proc_dointvec_userhz_jiffies(ctl, write, buffer, lenp, ppos); |
---|
3049 | 3516 | |
---|
.. | .. |
---|
3052 | 3519 | } |
---|
3053 | 3520 | |
---|
3054 | 3521 | int neigh_proc_dointvec_ms_jiffies(struct ctl_table *ctl, int write, |
---|
3055 | | - void __user *buffer, |
---|
3056 | | - size_t *lenp, loff_t *ppos) |
---|
| 3522 | + void *buffer, size_t *lenp, loff_t *ppos) |
---|
3057 | 3523 | { |
---|
3058 | 3524 | int ret = proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos); |
---|
3059 | 3525 | |
---|
.. | .. |
---|
3063 | 3529 | EXPORT_SYMBOL(neigh_proc_dointvec_ms_jiffies); |
---|
3064 | 3530 | |
---|
3065 | 3531 | static int neigh_proc_dointvec_unres_qlen(struct ctl_table *ctl, int write, |
---|
3066 | | - void __user *buffer, |
---|
3067 | | - size_t *lenp, loff_t *ppos) |
---|
| 3532 | + void *buffer, size_t *lenp, |
---|
| 3533 | + loff_t *ppos) |
---|
3068 | 3534 | { |
---|
3069 | 3535 | int ret = proc_unres_qlen(ctl, write, buffer, lenp, ppos); |
---|
3070 | 3536 | |
---|
.. | .. |
---|
3073 | 3539 | } |
---|
3074 | 3540 | |
---|
3075 | 3541 | static int neigh_proc_base_reachable_time(struct ctl_table *ctl, int write, |
---|
3076 | | - void __user *buffer, |
---|
3077 | | - size_t *lenp, loff_t *ppos) |
---|
| 3542 | + void *buffer, size_t *lenp, |
---|
| 3543 | + loff_t *ppos) |
---|
3078 | 3544 | { |
---|
3079 | 3545 | struct neigh_parms *p = ctl->extra2; |
---|
3080 | 3546 | int ret; |
---|
.. | .. |
---|
3118 | 3584 | #define NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(attr, name) \ |
---|
3119 | 3585 | NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_userhz_jiffies) |
---|
3120 | 3586 | |
---|
3121 | | -#define NEIGH_SYSCTL_MS_JIFFIES_ENTRY(attr, name) \ |
---|
3122 | | - NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_ms_jiffies) |
---|
3123 | | - |
---|
3124 | 3587 | #define NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(attr, data_attr, name) \ |
---|
3125 | 3588 | NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_ms_jiffies) |
---|
3126 | 3589 | |
---|
.. | .. |
---|
3158 | 3621 | .procname = "gc_thresh1", |
---|
3159 | 3622 | .maxlen = sizeof(int), |
---|
3160 | 3623 | .mode = 0644, |
---|
3161 | | - .extra1 = &zero, |
---|
3162 | | - .extra2 = &int_max, |
---|
| 3624 | + .extra1 = SYSCTL_ZERO, |
---|
| 3625 | + .extra2 = SYSCTL_INT_MAX, |
---|
3163 | 3626 | .proc_handler = proc_dointvec_minmax, |
---|
3164 | 3627 | }, |
---|
3165 | 3628 | [NEIGH_VAR_GC_THRESH2] = { |
---|
3166 | 3629 | .procname = "gc_thresh2", |
---|
3167 | 3630 | .maxlen = sizeof(int), |
---|
3168 | 3631 | .mode = 0644, |
---|
3169 | | - .extra1 = &zero, |
---|
3170 | | - .extra2 = &int_max, |
---|
| 3632 | + .extra1 = SYSCTL_ZERO, |
---|
| 3633 | + .extra2 = SYSCTL_INT_MAX, |
---|
3171 | 3634 | .proc_handler = proc_dointvec_minmax, |
---|
3172 | 3635 | }, |
---|
3173 | 3636 | [NEIGH_VAR_GC_THRESH3] = { |
---|
3174 | 3637 | .procname = "gc_thresh3", |
---|
3175 | 3638 | .maxlen = sizeof(int), |
---|
3176 | 3639 | .mode = 0644, |
---|
3177 | | - .extra1 = &zero, |
---|
3178 | | - .extra2 = &int_max, |
---|
| 3640 | + .extra1 = SYSCTL_ZERO, |
---|
| 3641 | + .extra2 = SYSCTL_INT_MAX, |
---|
3179 | 3642 | .proc_handler = proc_dointvec_minmax, |
---|
3180 | 3643 | }, |
---|
3181 | 3644 | {}, |
---|
.. | .. |
---|
3288 | 3751 | { |
---|
3289 | 3752 | rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL, 0); |
---|
3290 | 3753 | rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL, 0); |
---|
3291 | | - rtnl_register(PF_UNSPEC, RTM_GETNEIGH, NULL, neigh_dump_info, 0); |
---|
| 3754 | + rtnl_register(PF_UNSPEC, RTM_GETNEIGH, neigh_get, neigh_dump_info, 0); |
---|
3292 | 3755 | |
---|
3293 | 3756 | rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info, |
---|
3294 | 3757 | 0); |
---|