| .. | .. |
|---|
| 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); |
|---|