| .. | .. |
|---|
| 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_in_range(n->updated, tref, jiffies)) |
|---|
| 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 | |
|---|
| .. | .. |
|---|
| 206 | 262 | |
|---|
| 207 | 263 | static void neigh_add_timer(struct neighbour *n, unsigned long when) |
|---|
| 208 | 264 | { |
|---|
| 265 | + /* Use safe distance from the jiffies - LONG_MAX point while timer |
|---|
| 266 | + * is running in DELAY/PROBE state but still show to user space |
|---|
| 267 | + * large times in the past. |
|---|
| 268 | + */ |
|---|
| 269 | + unsigned long mint = jiffies - (LONG_MAX - 86400 * HZ); |
|---|
| 270 | + |
|---|
| 209 | 271 | neigh_hold(n); |
|---|
| 272 | + if (!time_in_range(n->confirmed, mint, jiffies)) |
|---|
| 273 | + n->confirmed = mint; |
|---|
| 274 | + if (time_before(n->used, n->confirmed)) |
|---|
| 275 | + n->used = n->confirmed; |
|---|
| 210 | 276 | if (unlikely(mod_timer(&n->timer, when))) { |
|---|
| 211 | 277 | printk("NEIGH: BUG, double timer add, state is %x\n", |
|---|
| 212 | 278 | n->nud_state); |
|---|
| .. | .. |
|---|
| 224 | 290 | return 0; |
|---|
| 225 | 291 | } |
|---|
| 226 | 292 | |
|---|
| 227 | | -static void pneigh_queue_purge(struct sk_buff_head *list) |
|---|
| 293 | +static void pneigh_queue_purge(struct sk_buff_head *list, struct net *net) |
|---|
| 228 | 294 | { |
|---|
| 295 | + struct sk_buff_head tmp; |
|---|
| 296 | + unsigned long flags; |
|---|
| 229 | 297 | struct sk_buff *skb; |
|---|
| 230 | 298 | |
|---|
| 231 | | - while ((skb = skb_dequeue(list)) != NULL) { |
|---|
| 299 | + skb_queue_head_init(&tmp); |
|---|
| 300 | + spin_lock_irqsave(&list->lock, flags); |
|---|
| 301 | + skb = skb_peek(list); |
|---|
| 302 | + while (skb != NULL) { |
|---|
| 303 | + struct sk_buff *skb_next = skb_peek_next(skb, list); |
|---|
| 304 | + if (net == NULL || net_eq(dev_net(skb->dev), net)) { |
|---|
| 305 | + __skb_unlink(skb, list); |
|---|
| 306 | + __skb_queue_tail(&tmp, skb); |
|---|
| 307 | + } |
|---|
| 308 | + skb = skb_next; |
|---|
| 309 | + } |
|---|
| 310 | + spin_unlock_irqrestore(&list->lock, flags); |
|---|
| 311 | + |
|---|
| 312 | + while ((skb = __skb_dequeue(&tmp))) { |
|---|
| 232 | 313 | dev_put(skb->dev); |
|---|
| 233 | 314 | kfree_skb(skb); |
|---|
| 234 | 315 | } |
|---|
| 235 | 316 | } |
|---|
| 236 | 317 | |
|---|
| 237 | | -static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev) |
|---|
| 318 | +static void neigh_flush_dev(struct neigh_table *tbl, struct net_device *dev, |
|---|
| 319 | + bool skip_perm) |
|---|
| 238 | 320 | { |
|---|
| 239 | 321 | int i; |
|---|
| 240 | 322 | struct neigh_hash_table *nht; |
|---|
| .. | .. |
|---|
| 252 | 334 | np = &n->next; |
|---|
| 253 | 335 | continue; |
|---|
| 254 | 336 | } |
|---|
| 337 | + if (skip_perm && n->nud_state & NUD_PERMANENT) { |
|---|
| 338 | + np = &n->next; |
|---|
| 339 | + continue; |
|---|
| 340 | + } |
|---|
| 255 | 341 | rcu_assign_pointer(*np, |
|---|
| 256 | 342 | rcu_dereference_protected(n->next, |
|---|
| 257 | 343 | lockdep_is_held(&tbl->lock))); |
|---|
| 258 | 344 | write_lock(&n->lock); |
|---|
| 259 | 345 | neigh_del_timer(n); |
|---|
| 260 | | - n->dead = 1; |
|---|
| 261 | | - |
|---|
| 346 | + neigh_mark_dead(n); |
|---|
| 262 | 347 | if (refcount_read(&n->refcnt) != 1) { |
|---|
| 263 | 348 | /* The most unpleasant situation. |
|---|
| 264 | 349 | We must destroy neighbour entry, |
|---|
| .. | .. |
|---|
| 287 | 372 | void neigh_changeaddr(struct neigh_table *tbl, struct net_device *dev) |
|---|
| 288 | 373 | { |
|---|
| 289 | 374 | write_lock_bh(&tbl->lock); |
|---|
| 290 | | - neigh_flush_dev(tbl, dev); |
|---|
| 375 | + neigh_flush_dev(tbl, dev, false); |
|---|
| 291 | 376 | write_unlock_bh(&tbl->lock); |
|---|
| 292 | 377 | } |
|---|
| 293 | 378 | EXPORT_SYMBOL(neigh_changeaddr); |
|---|
| 294 | 379 | |
|---|
| 295 | | -int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev) |
|---|
| 380 | +static int __neigh_ifdown(struct neigh_table *tbl, struct net_device *dev, |
|---|
| 381 | + bool skip_perm) |
|---|
| 296 | 382 | { |
|---|
| 297 | 383 | write_lock_bh(&tbl->lock); |
|---|
| 298 | | - neigh_flush_dev(tbl, dev); |
|---|
| 384 | + neigh_flush_dev(tbl, dev, skip_perm); |
|---|
| 299 | 385 | pneigh_ifdown_and_unlock(tbl, dev); |
|---|
| 386 | + pneigh_queue_purge(&tbl->proxy_queue, dev ? dev_net(dev) : NULL); |
|---|
| 387 | + if (skb_queue_empty_lockless(&tbl->proxy_queue)) |
|---|
| 388 | + del_timer_sync(&tbl->proxy_timer); |
|---|
| 389 | + return 0; |
|---|
| 390 | +} |
|---|
| 300 | 391 | |
|---|
| 301 | | - del_timer_sync(&tbl->proxy_timer); |
|---|
| 302 | | - pneigh_queue_purge(&tbl->proxy_queue); |
|---|
| 392 | +int neigh_carrier_down(struct neigh_table *tbl, struct net_device *dev) |
|---|
| 393 | +{ |
|---|
| 394 | + __neigh_ifdown(tbl, dev, true); |
|---|
| 395 | + return 0; |
|---|
| 396 | +} |
|---|
| 397 | +EXPORT_SYMBOL(neigh_carrier_down); |
|---|
| 398 | + |
|---|
| 399 | +int neigh_ifdown(struct neigh_table *tbl, struct net_device *dev) |
|---|
| 400 | +{ |
|---|
| 401 | + __neigh_ifdown(tbl, dev, false); |
|---|
| 303 | 402 | return 0; |
|---|
| 304 | 403 | } |
|---|
| 305 | 404 | EXPORT_SYMBOL(neigh_ifdown); |
|---|
| 306 | 405 | |
|---|
| 307 | | -static struct neighbour *neigh_alloc(struct neigh_table *tbl, struct net_device *dev) |
|---|
| 406 | +static struct neighbour *neigh_alloc(struct neigh_table *tbl, |
|---|
| 407 | + struct net_device *dev, |
|---|
| 408 | + u8 flags, bool exempt_from_gc) |
|---|
| 308 | 409 | { |
|---|
| 309 | 410 | struct neighbour *n = NULL; |
|---|
| 310 | 411 | unsigned long now = jiffies; |
|---|
| 311 | 412 | int entries; |
|---|
| 312 | 413 | |
|---|
| 313 | | - entries = atomic_inc_return(&tbl->entries) - 1; |
|---|
| 414 | + if (exempt_from_gc) |
|---|
| 415 | + goto do_alloc; |
|---|
| 416 | + |
|---|
| 417 | + entries = atomic_inc_return(&tbl->gc_entries) - 1; |
|---|
| 314 | 418 | if (entries >= tbl->gc_thresh3 || |
|---|
| 315 | 419 | (entries >= tbl->gc_thresh2 && |
|---|
| 316 | 420 | time_after(now, tbl->last_flush + 5 * HZ))) { |
|---|
| .. | .. |
|---|
| 323 | 427 | } |
|---|
| 324 | 428 | } |
|---|
| 325 | 429 | |
|---|
| 430 | +do_alloc: |
|---|
| 326 | 431 | n = kzalloc(tbl->entry_size + dev->neigh_priv_len, GFP_ATOMIC); |
|---|
| 327 | 432 | if (!n) |
|---|
| 328 | 433 | goto out_entries; |
|---|
| .. | .. |
|---|
| 333 | 438 | n->updated = n->used = now; |
|---|
| 334 | 439 | n->nud_state = NUD_NONE; |
|---|
| 335 | 440 | n->output = neigh_blackhole; |
|---|
| 441 | + n->flags = flags; |
|---|
| 336 | 442 | seqlock_init(&n->hh.hh_lock); |
|---|
| 337 | 443 | n->parms = neigh_parms_clone(&tbl->parms); |
|---|
| 338 | 444 | timer_setup(&n->timer, neigh_timer_handler, 0); |
|---|
| .. | .. |
|---|
| 341 | 447 | n->tbl = tbl; |
|---|
| 342 | 448 | refcount_set(&n->refcnt, 1); |
|---|
| 343 | 449 | n->dead = 1; |
|---|
| 450 | + INIT_LIST_HEAD(&n->gc_list); |
|---|
| 451 | + |
|---|
| 452 | + atomic_inc(&tbl->entries); |
|---|
| 344 | 453 | out: |
|---|
| 345 | 454 | return n; |
|---|
| 346 | 455 | |
|---|
| 347 | 456 | out_entries: |
|---|
| 348 | | - atomic_dec(&tbl->entries); |
|---|
| 457 | + if (!exempt_from_gc) |
|---|
| 458 | + atomic_dec(&tbl->gc_entries); |
|---|
| 349 | 459 | goto out; |
|---|
| 350 | 460 | } |
|---|
| 351 | 461 | |
|---|
| .. | .. |
|---|
| 461 | 571 | } |
|---|
| 462 | 572 | EXPORT_SYMBOL(neigh_lookup); |
|---|
| 463 | 573 | |
|---|
| 464 | | -struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net, |
|---|
| 465 | | - const void *pkey) |
|---|
| 574 | +static struct neighbour * |
|---|
| 575 | +___neigh_create(struct neigh_table *tbl, const void *pkey, |
|---|
| 576 | + struct net_device *dev, u8 flags, |
|---|
| 577 | + bool exempt_from_gc, bool want_ref) |
|---|
| 466 | 578 | { |
|---|
| 467 | | - struct neighbour *n; |
|---|
| 468 | | - unsigned int key_len = tbl->key_len; |
|---|
| 469 | | - u32 hash_val; |
|---|
| 579 | + u32 hash_val, key_len = tbl->key_len; |
|---|
| 580 | + struct neighbour *n1, *rc, *n; |
|---|
| 470 | 581 | struct neigh_hash_table *nht; |
|---|
| 471 | | - |
|---|
| 472 | | - NEIGH_CACHE_STAT_INC(tbl, lookups); |
|---|
| 473 | | - |
|---|
| 474 | | - rcu_read_lock_bh(); |
|---|
| 475 | | - nht = rcu_dereference_bh(tbl->nht); |
|---|
| 476 | | - hash_val = tbl->hash(pkey, NULL, nht->hash_rnd) >> (32 - nht->hash_shift); |
|---|
| 477 | | - |
|---|
| 478 | | - for (n = rcu_dereference_bh(nht->hash_buckets[hash_val]); |
|---|
| 479 | | - n != NULL; |
|---|
| 480 | | - n = rcu_dereference_bh(n->next)) { |
|---|
| 481 | | - if (!memcmp(n->primary_key, pkey, key_len) && |
|---|
| 482 | | - net_eq(dev_net(n->dev), net)) { |
|---|
| 483 | | - if (!refcount_inc_not_zero(&n->refcnt)) |
|---|
| 484 | | - n = NULL; |
|---|
| 485 | | - NEIGH_CACHE_STAT_INC(tbl, hits); |
|---|
| 486 | | - break; |
|---|
| 487 | | - } |
|---|
| 488 | | - } |
|---|
| 489 | | - |
|---|
| 490 | | - rcu_read_unlock_bh(); |
|---|
| 491 | | - return n; |
|---|
| 492 | | -} |
|---|
| 493 | | -EXPORT_SYMBOL(neigh_lookup_nodev); |
|---|
| 494 | | - |
|---|
| 495 | | -struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey, |
|---|
| 496 | | - struct net_device *dev, bool want_ref) |
|---|
| 497 | | -{ |
|---|
| 498 | | - u32 hash_val; |
|---|
| 499 | | - unsigned int key_len = tbl->key_len; |
|---|
| 500 | 582 | int error; |
|---|
| 501 | | - struct neighbour *n1, *rc, *n = neigh_alloc(tbl, dev); |
|---|
| 502 | | - struct neigh_hash_table *nht; |
|---|
| 503 | 583 | |
|---|
| 584 | + n = neigh_alloc(tbl, dev, flags, exempt_from_gc); |
|---|
| 585 | + trace_neigh_create(tbl, dev, pkey, n, exempt_from_gc); |
|---|
| 504 | 586 | if (!n) { |
|---|
| 505 | 587 | rc = ERR_PTR(-ENOBUFS); |
|---|
| 506 | 588 | goto out; |
|---|
| .. | .. |
|---|
| 561 | 643 | } |
|---|
| 562 | 644 | |
|---|
| 563 | 645 | n->dead = 0; |
|---|
| 646 | + if (!exempt_from_gc) |
|---|
| 647 | + list_add_tail(&n->gc_list, &n->tbl->gc_list); |
|---|
| 648 | + |
|---|
| 564 | 649 | if (want_ref) |
|---|
| 565 | 650 | neigh_hold(n); |
|---|
| 566 | 651 | rcu_assign_pointer(n->next, |
|---|
| .. | .. |
|---|
| 575 | 660 | out_tbl_unlock: |
|---|
| 576 | 661 | write_unlock_bh(&tbl->lock); |
|---|
| 577 | 662 | out_neigh_release: |
|---|
| 663 | + if (!exempt_from_gc) |
|---|
| 664 | + atomic_dec(&tbl->gc_entries); |
|---|
| 578 | 665 | neigh_release(n); |
|---|
| 579 | 666 | goto out; |
|---|
| 667 | +} |
|---|
| 668 | + |
|---|
| 669 | +struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey, |
|---|
| 670 | + struct net_device *dev, bool want_ref) |
|---|
| 671 | +{ |
|---|
| 672 | + return ___neigh_create(tbl, pkey, dev, 0, false, want_ref); |
|---|
| 580 | 673 | } |
|---|
| 581 | 674 | EXPORT_SYMBOL(__neigh_create); |
|---|
| 582 | 675 | |
|---|
| .. | .. |
|---|
| 834 | 927 | goto next_elt; |
|---|
| 835 | 928 | } |
|---|
| 836 | 929 | |
|---|
| 837 | | - if (time_before(n->used, n->confirmed)) |
|---|
| 930 | + if (time_before(n->used, n->confirmed) && |
|---|
| 931 | + time_is_before_eq_jiffies(n->confirmed)) |
|---|
| 838 | 932 | n->used = n->confirmed; |
|---|
| 839 | 933 | |
|---|
| 840 | 934 | if (refcount_read(&n->refcnt) == 1 && |
|---|
| 841 | 935 | (state == NUD_FAILED || |
|---|
| 842 | | - time_after(jiffies, n->used + NEIGH_VAR(n->parms, GC_STALETIME)))) { |
|---|
| 843 | | - *np = n->next; |
|---|
| 844 | | - n->dead = 1; |
|---|
| 936 | + !time_in_range_open(jiffies, n->used, |
|---|
| 937 | + n->used + NEIGH_VAR(n->parms, GC_STALETIME)))) { |
|---|
| 938 | + rcu_assign_pointer(*np, |
|---|
| 939 | + rcu_dereference_protected(n->next, |
|---|
| 940 | + lockdep_is_held(&tbl->lock))); |
|---|
| 941 | + neigh_mark_dead(n); |
|---|
| 845 | 942 | write_unlock(&n->lock); |
|---|
| 846 | 943 | neigh_cleanup_and_release(n); |
|---|
| 847 | 944 | continue; |
|---|
| .. | .. |
|---|
| 915 | 1012 | if (neigh->ops->solicit) |
|---|
| 916 | 1013 | neigh->ops->solicit(neigh, skb); |
|---|
| 917 | 1014 | atomic_inc(&neigh->probes); |
|---|
| 918 | | - kfree_skb(skb); |
|---|
| 1015 | + consume_skb(skb); |
|---|
| 919 | 1016 | } |
|---|
| 920 | 1017 | |
|---|
| 921 | 1018 | /* Called when a timer expires for a neighbour entry. */ |
|---|
| .. | .. |
|---|
| 972 | 1069 | neigh->updated = jiffies; |
|---|
| 973 | 1070 | atomic_set(&neigh->probes, 0); |
|---|
| 974 | 1071 | notify = 1; |
|---|
| 975 | | - next = now + NEIGH_VAR(neigh->parms, RETRANS_TIME); |
|---|
| 1072 | + next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), |
|---|
| 1073 | + HZ/100); |
|---|
| 976 | 1074 | } |
|---|
| 977 | 1075 | } else { |
|---|
| 978 | 1076 | /* NUD_PROBE|NUD_INCOMPLETE */ |
|---|
| 979 | | - next = now + NEIGH_VAR(neigh->parms, RETRANS_TIME); |
|---|
| 1077 | + next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), HZ/100); |
|---|
| 980 | 1078 | } |
|---|
| 981 | 1079 | |
|---|
| 982 | 1080 | if ((neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) && |
|---|
| .. | .. |
|---|
| 988 | 1086 | } |
|---|
| 989 | 1087 | |
|---|
| 990 | 1088 | if (neigh->nud_state & NUD_IN_TIMER) { |
|---|
| 991 | | - if (time_before(next, jiffies + HZ/2)) |
|---|
| 992 | | - next = jiffies + HZ/2; |
|---|
| 1089 | + if (time_before(next, jiffies + HZ/100)) |
|---|
| 1090 | + next = jiffies + HZ/100; |
|---|
| 993 | 1091 | if (!mod_timer(&neigh->timer, next)) |
|---|
| 994 | 1092 | neigh_hold(neigh); |
|---|
| 995 | 1093 | } |
|---|
| .. | .. |
|---|
| 1002 | 1100 | |
|---|
| 1003 | 1101 | if (notify) |
|---|
| 1004 | 1102 | neigh_update_notify(neigh, 0); |
|---|
| 1103 | + |
|---|
| 1104 | + trace_neigh_timer_handler(neigh, 0); |
|---|
| 1005 | 1105 | |
|---|
| 1006 | 1106 | neigh_release(neigh); |
|---|
| 1007 | 1107 | } |
|---|
| .. | .. |
|---|
| 1030 | 1130 | neigh->nud_state = NUD_INCOMPLETE; |
|---|
| 1031 | 1131 | neigh->updated = now; |
|---|
| 1032 | 1132 | next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), |
|---|
| 1033 | | - HZ/2); |
|---|
| 1133 | + HZ/100); |
|---|
| 1034 | 1134 | neigh_add_timer(neigh, next); |
|---|
| 1035 | 1135 | immediate_probe = true; |
|---|
| 1036 | 1136 | } else { |
|---|
| .. | .. |
|---|
| 1075 | 1175 | else |
|---|
| 1076 | 1176 | write_unlock(&neigh->lock); |
|---|
| 1077 | 1177 | local_bh_enable(); |
|---|
| 1178 | + trace_neigh_event_send_done(neigh, rc); |
|---|
| 1078 | 1179 | return rc; |
|---|
| 1079 | 1180 | |
|---|
| 1080 | 1181 | out_dead: |
|---|
| .. | .. |
|---|
| 1082 | 1183 | goto out_unlock_bh; |
|---|
| 1083 | 1184 | write_unlock_bh(&neigh->lock); |
|---|
| 1084 | 1185 | kfree_skb(skb); |
|---|
| 1186 | + trace_neigh_event_send_dead(neigh, 1); |
|---|
| 1085 | 1187 | return 1; |
|---|
| 1086 | 1188 | } |
|---|
| 1087 | 1189 | EXPORT_SYMBOL(__neigh_event_send); |
|---|
| .. | .. |
|---|
| 1117 | 1219 | lladdr instead of overriding it |
|---|
| 1118 | 1220 | if it is different. |
|---|
| 1119 | 1221 | NEIGH_UPDATE_F_ADMIN means that the change is administrative. |
|---|
| 1120 | | - |
|---|
| 1222 | + NEIGH_UPDATE_F_USE means that the entry is user triggered. |
|---|
| 1121 | 1223 | NEIGH_UPDATE_F_OVERRIDE_ISROUTER allows to override existing |
|---|
| 1122 | 1224 | NTF_ROUTER flag. |
|---|
| 1123 | 1225 | NEIGH_UPDATE_F_ISROUTER indicates if the neighbour is known as |
|---|
| .. | .. |
|---|
| 1126 | 1228 | Caller MUST hold reference count on the entry. |
|---|
| 1127 | 1229 | */ |
|---|
| 1128 | 1230 | |
|---|
| 1129 | | -int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, |
|---|
| 1130 | | - u32 flags, u32 nlmsg_pid) |
|---|
| 1231 | +static int __neigh_update(struct neighbour *neigh, const u8 *lladdr, |
|---|
| 1232 | + u8 new, u32 flags, u32 nlmsg_pid, |
|---|
| 1233 | + struct netlink_ext_ack *extack) |
|---|
| 1131 | 1234 | { |
|---|
| 1235 | + bool ext_learn_change = false; |
|---|
| 1132 | 1236 | u8 old; |
|---|
| 1133 | 1237 | int err; |
|---|
| 1134 | 1238 | int notify = 0; |
|---|
| 1135 | 1239 | struct net_device *dev; |
|---|
| 1136 | 1240 | int update_isrouter = 0; |
|---|
| 1241 | + |
|---|
| 1242 | + trace_neigh_update(neigh, lladdr, new, flags, nlmsg_pid); |
|---|
| 1137 | 1243 | |
|---|
| 1138 | 1244 | write_lock_bh(&neigh->lock); |
|---|
| 1139 | 1245 | |
|---|
| .. | .. |
|---|
| 1141 | 1247 | old = neigh->nud_state; |
|---|
| 1142 | 1248 | err = -EPERM; |
|---|
| 1143 | 1249 | |
|---|
| 1250 | + if (neigh->dead) { |
|---|
| 1251 | + NL_SET_ERR_MSG(extack, "Neighbor entry is now dead"); |
|---|
| 1252 | + new = old; |
|---|
| 1253 | + goto out; |
|---|
| 1254 | + } |
|---|
| 1144 | 1255 | if (!(flags & NEIGH_UPDATE_F_ADMIN) && |
|---|
| 1145 | 1256 | (old & (NUD_NOARP | NUD_PERMANENT))) |
|---|
| 1146 | 1257 | goto out; |
|---|
| 1147 | | - if (neigh->dead) |
|---|
| 1148 | | - goto out; |
|---|
| 1149 | 1258 | |
|---|
| 1150 | | - neigh_update_ext_learned(neigh, flags, ¬ify); |
|---|
| 1259 | + ext_learn_change = neigh_update_ext_learned(neigh, flags, ¬ify); |
|---|
| 1260 | + if (flags & NEIGH_UPDATE_F_USE) { |
|---|
| 1261 | + new = old & ~NUD_PERMANENT; |
|---|
| 1262 | + neigh->nud_state = new; |
|---|
| 1263 | + err = 0; |
|---|
| 1264 | + goto out; |
|---|
| 1265 | + } |
|---|
| 1151 | 1266 | |
|---|
| 1152 | 1267 | if (!(new & NUD_VALID)) { |
|---|
| 1153 | 1268 | neigh_del_timer(neigh); |
|---|
| .. | .. |
|---|
| 1182 | 1297 | use it, otherwise discard the request. |
|---|
| 1183 | 1298 | */ |
|---|
| 1184 | 1299 | err = -EINVAL; |
|---|
| 1185 | | - if (!(old & NUD_VALID)) |
|---|
| 1300 | + if (!(old & NUD_VALID)) { |
|---|
| 1301 | + NL_SET_ERR_MSG(extack, "No link layer address given"); |
|---|
| 1186 | 1302 | goto out; |
|---|
| 1303 | + } |
|---|
| 1187 | 1304 | lladdr = neigh->ha; |
|---|
| 1188 | 1305 | } |
|---|
| 1189 | 1306 | |
|---|
| .. | .. |
|---|
| 1287 | 1404 | neigh->arp_queue_len_bytes = 0; |
|---|
| 1288 | 1405 | } |
|---|
| 1289 | 1406 | out: |
|---|
| 1290 | | - if (update_isrouter) { |
|---|
| 1291 | | - neigh->flags = (flags & NEIGH_UPDATE_F_ISROUTER) ? |
|---|
| 1292 | | - (neigh->flags | NTF_ROUTER) : |
|---|
| 1293 | | - (neigh->flags & ~NTF_ROUTER); |
|---|
| 1294 | | - } |
|---|
| 1407 | + if (update_isrouter) |
|---|
| 1408 | + neigh_update_is_router(neigh, flags, ¬ify); |
|---|
| 1295 | 1409 | write_unlock_bh(&neigh->lock); |
|---|
| 1410 | + |
|---|
| 1411 | + if (((new ^ old) & NUD_PERMANENT) || ext_learn_change) |
|---|
| 1412 | + neigh_update_gc_list(neigh); |
|---|
| 1296 | 1413 | |
|---|
| 1297 | 1414 | if (notify) |
|---|
| 1298 | 1415 | neigh_update_notify(neigh, nlmsg_pid); |
|---|
| 1299 | 1416 | |
|---|
| 1417 | + trace_neigh_update_done(neigh, err); |
|---|
| 1418 | + |
|---|
| 1300 | 1419 | return err; |
|---|
| 1420 | +} |
|---|
| 1421 | + |
|---|
| 1422 | +int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new, |
|---|
| 1423 | + u32 flags, u32 nlmsg_pid) |
|---|
| 1424 | +{ |
|---|
| 1425 | + return __neigh_update(neigh, lladdr, new, flags, nlmsg_pid, NULL); |
|---|
| 1301 | 1426 | } |
|---|
| 1302 | 1427 | EXPORT_SYMBOL(neigh_update); |
|---|
| 1303 | 1428 | |
|---|
| .. | .. |
|---|
| 1314 | 1439 | neigh->nud_state = NUD_INCOMPLETE; |
|---|
| 1315 | 1440 | atomic_set(&neigh->probes, neigh_max_probes(neigh)); |
|---|
| 1316 | 1441 | neigh_add_timer(neigh, |
|---|
| 1317 | | - jiffies + NEIGH_VAR(neigh->parms, RETRANS_TIME)); |
|---|
| 1442 | + jiffies + max(NEIGH_VAR(neigh->parms, RETRANS_TIME), |
|---|
| 1443 | + HZ/100)); |
|---|
| 1318 | 1444 | } |
|---|
| 1319 | 1445 | EXPORT_SYMBOL(__neigh_set_probe_once); |
|---|
| 1320 | 1446 | |
|---|
| .. | .. |
|---|
| 1563 | 1689 | unsigned long phsize; |
|---|
| 1564 | 1690 | |
|---|
| 1565 | 1691 | INIT_LIST_HEAD(&tbl->parms_list); |
|---|
| 1692 | + INIT_LIST_HEAD(&tbl->gc_list); |
|---|
| 1566 | 1693 | list_add(&tbl->parms.list, &tbl->parms_list); |
|---|
| 1567 | 1694 | write_pnet(&tbl->parms.net, &init_net); |
|---|
| 1568 | 1695 | refcount_set(&tbl->parms.refcnt, 1); |
|---|
| .. | .. |
|---|
| 1614 | 1741 | /* It is not clean... Fix it to unload IPv6 module safely */ |
|---|
| 1615 | 1742 | cancel_delayed_work_sync(&tbl->gc_work); |
|---|
| 1616 | 1743 | del_timer_sync(&tbl->proxy_timer); |
|---|
| 1617 | | - pneigh_queue_purge(&tbl->proxy_queue); |
|---|
| 1744 | + pneigh_queue_purge(&tbl->proxy_queue, NULL); |
|---|
| 1618 | 1745 | neigh_ifdown(tbl, NULL); |
|---|
| 1619 | 1746 | if (atomic_read(&tbl->entries)) |
|---|
| 1620 | 1747 | pr_crit("neighbour leakage\n"); |
|---|
| .. | .. |
|---|
| 1646 | 1773 | case AF_INET6: |
|---|
| 1647 | 1774 | tbl = neigh_tables[NEIGH_ND_TABLE]; |
|---|
| 1648 | 1775 | break; |
|---|
| 1649 | | - case AF_DECnet: |
|---|
| 1650 | | - tbl = neigh_tables[NEIGH_DN_TABLE]; |
|---|
| 1651 | | - break; |
|---|
| 1652 | 1776 | } |
|---|
| 1653 | 1777 | |
|---|
| 1654 | 1778 | return tbl; |
|---|
| 1655 | 1779 | } |
|---|
| 1780 | + |
|---|
| 1781 | +const struct nla_policy nda_policy[NDA_MAX+1] = { |
|---|
| 1782 | + [NDA_UNSPEC] = { .strict_start_type = NDA_NH_ID }, |
|---|
| 1783 | + [NDA_DST] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, |
|---|
| 1784 | + [NDA_LLADDR] = { .type = NLA_BINARY, .len = MAX_ADDR_LEN }, |
|---|
| 1785 | + [NDA_CACHEINFO] = { .len = sizeof(struct nda_cacheinfo) }, |
|---|
| 1786 | + [NDA_PROBES] = { .type = NLA_U32 }, |
|---|
| 1787 | + [NDA_VLAN] = { .type = NLA_U16 }, |
|---|
| 1788 | + [NDA_PORT] = { .type = NLA_U16 }, |
|---|
| 1789 | + [NDA_VNI] = { .type = NLA_U32 }, |
|---|
| 1790 | + [NDA_IFINDEX] = { .type = NLA_U32 }, |
|---|
| 1791 | + [NDA_MASTER] = { .type = NLA_U32 }, |
|---|
| 1792 | + [NDA_PROTOCOL] = { .type = NLA_U8 }, |
|---|
| 1793 | + [NDA_NH_ID] = { .type = NLA_U32 }, |
|---|
| 1794 | + [NDA_FDB_EXT_ATTRS] = { .type = NLA_NESTED }, |
|---|
| 1795 | +}; |
|---|
| 1656 | 1796 | |
|---|
| 1657 | 1797 | static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, |
|---|
| 1658 | 1798 | struct netlink_ext_ack *extack) |
|---|
| .. | .. |
|---|
| 1670 | 1810 | goto out; |
|---|
| 1671 | 1811 | |
|---|
| 1672 | 1812 | dst_attr = nlmsg_find_attr(nlh, sizeof(*ndm), NDA_DST); |
|---|
| 1673 | | - if (dst_attr == NULL) |
|---|
| 1813 | + if (!dst_attr) { |
|---|
| 1814 | + NL_SET_ERR_MSG(extack, "Network address not specified"); |
|---|
| 1674 | 1815 | goto out; |
|---|
| 1816 | + } |
|---|
| 1675 | 1817 | |
|---|
| 1676 | 1818 | ndm = nlmsg_data(nlh); |
|---|
| 1677 | 1819 | if (ndm->ndm_ifindex) { |
|---|
| .. | .. |
|---|
| 1686 | 1828 | if (tbl == NULL) |
|---|
| 1687 | 1829 | return -EAFNOSUPPORT; |
|---|
| 1688 | 1830 | |
|---|
| 1689 | | - if (nla_len(dst_attr) < (int)tbl->key_len) |
|---|
| 1831 | + if (nla_len(dst_attr) < (int)tbl->key_len) { |
|---|
| 1832 | + NL_SET_ERR_MSG(extack, "Invalid network address"); |
|---|
| 1690 | 1833 | goto out; |
|---|
| 1834 | + } |
|---|
| 1691 | 1835 | |
|---|
| 1692 | 1836 | if (ndm->ndm_flags & NTF_PROXY) { |
|---|
| 1693 | 1837 | err = pneigh_delete(tbl, net, nla_data(dst_attr), dev); |
|---|
| .. | .. |
|---|
| 1703 | 1847 | goto out; |
|---|
| 1704 | 1848 | } |
|---|
| 1705 | 1849 | |
|---|
| 1706 | | - err = neigh_update(neigh, NULL, NUD_FAILED, |
|---|
| 1707 | | - NEIGH_UPDATE_F_OVERRIDE | |
|---|
| 1708 | | - NEIGH_UPDATE_F_ADMIN, |
|---|
| 1709 | | - NETLINK_CB(skb).portid); |
|---|
| 1850 | + err = __neigh_update(neigh, NULL, NUD_FAILED, |
|---|
| 1851 | + NEIGH_UPDATE_F_OVERRIDE | NEIGH_UPDATE_F_ADMIN, |
|---|
| 1852 | + NETLINK_CB(skb).portid, extack); |
|---|
| 1710 | 1853 | write_lock_bh(&tbl->lock); |
|---|
| 1711 | 1854 | neigh_release(neigh); |
|---|
| 1712 | 1855 | neigh_remove_one(neigh, tbl); |
|---|
| .. | .. |
|---|
| 1719 | 1862 | static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, |
|---|
| 1720 | 1863 | struct netlink_ext_ack *extack) |
|---|
| 1721 | 1864 | { |
|---|
| 1722 | | - int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE; |
|---|
| 1865 | + int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE | |
|---|
| 1866 | + NEIGH_UPDATE_F_OVERRIDE_ISROUTER; |
|---|
| 1723 | 1867 | struct net *net = sock_net(skb->sk); |
|---|
| 1724 | 1868 | struct ndmsg *ndm; |
|---|
| 1725 | 1869 | struct nlattr *tb[NDA_MAX+1]; |
|---|
| .. | .. |
|---|
| 1727 | 1871 | struct net_device *dev = NULL; |
|---|
| 1728 | 1872 | struct neighbour *neigh; |
|---|
| 1729 | 1873 | void *dst, *lladdr; |
|---|
| 1874 | + u8 protocol = 0; |
|---|
| 1730 | 1875 | int err; |
|---|
| 1731 | 1876 | |
|---|
| 1732 | 1877 | ASSERT_RTNL(); |
|---|
| 1733 | | - err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL, extack); |
|---|
| 1878 | + err = nlmsg_parse_deprecated(nlh, sizeof(*ndm), tb, NDA_MAX, |
|---|
| 1879 | + nda_policy, extack); |
|---|
| 1734 | 1880 | if (err < 0) |
|---|
| 1735 | 1881 | goto out; |
|---|
| 1736 | 1882 | |
|---|
| 1737 | 1883 | err = -EINVAL; |
|---|
| 1738 | | - if (tb[NDA_DST] == NULL) |
|---|
| 1884 | + if (!tb[NDA_DST]) { |
|---|
| 1885 | + NL_SET_ERR_MSG(extack, "Network address not specified"); |
|---|
| 1739 | 1886 | goto out; |
|---|
| 1887 | + } |
|---|
| 1740 | 1888 | |
|---|
| 1741 | 1889 | ndm = nlmsg_data(nlh); |
|---|
| 1742 | 1890 | if (ndm->ndm_ifindex) { |
|---|
| .. | .. |
|---|
| 1746 | 1894 | goto out; |
|---|
| 1747 | 1895 | } |
|---|
| 1748 | 1896 | |
|---|
| 1749 | | - if (tb[NDA_LLADDR] && nla_len(tb[NDA_LLADDR]) < dev->addr_len) |
|---|
| 1897 | + if (tb[NDA_LLADDR] && nla_len(tb[NDA_LLADDR]) < dev->addr_len) { |
|---|
| 1898 | + NL_SET_ERR_MSG(extack, "Invalid link address"); |
|---|
| 1750 | 1899 | goto out; |
|---|
| 1900 | + } |
|---|
| 1751 | 1901 | } |
|---|
| 1752 | 1902 | |
|---|
| 1753 | 1903 | tbl = neigh_find_table(ndm->ndm_family); |
|---|
| 1754 | 1904 | if (tbl == NULL) |
|---|
| 1755 | 1905 | return -EAFNOSUPPORT; |
|---|
| 1756 | 1906 | |
|---|
| 1757 | | - if (nla_len(tb[NDA_DST]) < (int)tbl->key_len) |
|---|
| 1907 | + if (nla_len(tb[NDA_DST]) < (int)tbl->key_len) { |
|---|
| 1908 | + NL_SET_ERR_MSG(extack, "Invalid network address"); |
|---|
| 1758 | 1909 | goto out; |
|---|
| 1910 | + } |
|---|
| 1911 | + |
|---|
| 1759 | 1912 | dst = nla_data(tb[NDA_DST]); |
|---|
| 1760 | 1913 | lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL; |
|---|
| 1914 | + |
|---|
| 1915 | + if (tb[NDA_PROTOCOL]) |
|---|
| 1916 | + protocol = nla_get_u8(tb[NDA_PROTOCOL]); |
|---|
| 1761 | 1917 | |
|---|
| 1762 | 1918 | if (ndm->ndm_flags & NTF_PROXY) { |
|---|
| 1763 | 1919 | struct pneigh_entry *pn; |
|---|
| .. | .. |
|---|
| 1766 | 1922 | pn = pneigh_lookup(tbl, net, dst, dev, 1); |
|---|
| 1767 | 1923 | if (pn) { |
|---|
| 1768 | 1924 | pn->flags = ndm->ndm_flags; |
|---|
| 1925 | + if (protocol) |
|---|
| 1926 | + pn->protocol = protocol; |
|---|
| 1769 | 1927 | err = 0; |
|---|
| 1770 | 1928 | } |
|---|
| 1771 | 1929 | goto out; |
|---|
| 1772 | 1930 | } |
|---|
| 1773 | 1931 | |
|---|
| 1774 | | - if (dev == NULL) |
|---|
| 1932 | + if (!dev) { |
|---|
| 1933 | + NL_SET_ERR_MSG(extack, "Device not specified"); |
|---|
| 1775 | 1934 | goto out; |
|---|
| 1935 | + } |
|---|
| 1936 | + |
|---|
| 1937 | + if (tbl->allow_add && !tbl->allow_add(dev, extack)) { |
|---|
| 1938 | + err = -EINVAL; |
|---|
| 1939 | + goto out; |
|---|
| 1940 | + } |
|---|
| 1776 | 1941 | |
|---|
| 1777 | 1942 | neigh = neigh_lookup(tbl, dst, dev); |
|---|
| 1778 | 1943 | if (neigh == NULL) { |
|---|
| 1944 | + bool exempt_from_gc; |
|---|
| 1945 | + |
|---|
| 1779 | 1946 | if (!(nlh->nlmsg_flags & NLM_F_CREATE)) { |
|---|
| 1780 | 1947 | err = -ENOENT; |
|---|
| 1781 | 1948 | goto out; |
|---|
| 1782 | 1949 | } |
|---|
| 1783 | 1950 | |
|---|
| 1784 | | - neigh = __neigh_lookup_errno(tbl, dst, dev); |
|---|
| 1951 | + exempt_from_gc = ndm->ndm_state & NUD_PERMANENT || |
|---|
| 1952 | + ndm->ndm_flags & NTF_EXT_LEARNED; |
|---|
| 1953 | + neigh = ___neigh_create(tbl, dst, dev, |
|---|
| 1954 | + ndm->ndm_flags & NTF_EXT_LEARNED, |
|---|
| 1955 | + exempt_from_gc, true); |
|---|
| 1785 | 1956 | if (IS_ERR(neigh)) { |
|---|
| 1786 | 1957 | err = PTR_ERR(neigh); |
|---|
| 1787 | 1958 | goto out; |
|---|
| .. | .. |
|---|
| 1794 | 1965 | } |
|---|
| 1795 | 1966 | |
|---|
| 1796 | 1967 | if (!(nlh->nlmsg_flags & NLM_F_REPLACE)) |
|---|
| 1797 | | - flags &= ~NEIGH_UPDATE_F_OVERRIDE; |
|---|
| 1968 | + flags &= ~(NEIGH_UPDATE_F_OVERRIDE | |
|---|
| 1969 | + NEIGH_UPDATE_F_OVERRIDE_ISROUTER); |
|---|
| 1798 | 1970 | } |
|---|
| 1799 | 1971 | |
|---|
| 1972 | + if (protocol) |
|---|
| 1973 | + neigh->protocol = protocol; |
|---|
| 1800 | 1974 | if (ndm->ndm_flags & NTF_EXT_LEARNED) |
|---|
| 1801 | 1975 | flags |= NEIGH_UPDATE_F_EXT_LEARNED; |
|---|
| 1976 | + if (ndm->ndm_flags & NTF_ROUTER) |
|---|
| 1977 | + flags |= NEIGH_UPDATE_F_ISROUTER; |
|---|
| 1978 | + if (ndm->ndm_flags & NTF_USE) |
|---|
| 1979 | + flags |= NEIGH_UPDATE_F_USE; |
|---|
| 1802 | 1980 | |
|---|
| 1803 | | - if (ndm->ndm_flags & NTF_USE) { |
|---|
| 1981 | + err = __neigh_update(neigh, lladdr, ndm->ndm_state, flags, |
|---|
| 1982 | + NETLINK_CB(skb).portid, extack); |
|---|
| 1983 | + if (!err && ndm->ndm_flags & NTF_USE) { |
|---|
| 1804 | 1984 | neigh_event_send(neigh, NULL); |
|---|
| 1805 | 1985 | err = 0; |
|---|
| 1806 | | - } else |
|---|
| 1807 | | - err = neigh_update(neigh, lladdr, ndm->ndm_state, flags, |
|---|
| 1808 | | - NETLINK_CB(skb).portid); |
|---|
| 1986 | + } |
|---|
| 1809 | 1987 | neigh_release(neigh); |
|---|
| 1810 | | - |
|---|
| 1811 | 1988 | out: |
|---|
| 1812 | 1989 | return err; |
|---|
| 1813 | 1990 | } |
|---|
| .. | .. |
|---|
| 1816 | 1993 | { |
|---|
| 1817 | 1994 | struct nlattr *nest; |
|---|
| 1818 | 1995 | |
|---|
| 1819 | | - nest = nla_nest_start(skb, NDTA_PARMS); |
|---|
| 1996 | + nest = nla_nest_start_noflag(skb, NDTA_PARMS); |
|---|
| 1820 | 1997 | if (nest == NULL) |
|---|
| 1821 | 1998 | return -ENOBUFS; |
|---|
| 1822 | 1999 | |
|---|
| .. | .. |
|---|
| 2018 | 2195 | bool found = false; |
|---|
| 2019 | 2196 | int err, tidx; |
|---|
| 2020 | 2197 | |
|---|
| 2021 | | - err = nlmsg_parse(nlh, sizeof(*ndtmsg), tb, NDTA_MAX, |
|---|
| 2022 | | - nl_neightbl_policy, extack); |
|---|
| 2198 | + err = nlmsg_parse_deprecated(nlh, sizeof(*ndtmsg), tb, NDTA_MAX, |
|---|
| 2199 | + nl_neightbl_policy, extack); |
|---|
| 2023 | 2200 | if (err < 0) |
|---|
| 2024 | 2201 | goto errout; |
|---|
| 2025 | 2202 | |
|---|
| .. | .. |
|---|
| 2056 | 2233 | struct neigh_parms *p; |
|---|
| 2057 | 2234 | int i, ifindex = 0; |
|---|
| 2058 | 2235 | |
|---|
| 2059 | | - err = nla_parse_nested(tbp, NDTPA_MAX, tb[NDTA_PARMS], |
|---|
| 2060 | | - nl_ntbl_parm_policy, extack); |
|---|
| 2236 | + err = nla_parse_nested_deprecated(tbp, NDTPA_MAX, |
|---|
| 2237 | + tb[NDTA_PARMS], |
|---|
| 2238 | + nl_ntbl_parm_policy, extack); |
|---|
| 2061 | 2239 | if (err < 0) |
|---|
| 2062 | 2240 | goto errout_tbl_lock; |
|---|
| 2063 | 2241 | |
|---|
| .. | .. |
|---|
| 2169 | 2347 | return err; |
|---|
| 2170 | 2348 | } |
|---|
| 2171 | 2349 | |
|---|
| 2350 | +static int neightbl_valid_dump_info(const struct nlmsghdr *nlh, |
|---|
| 2351 | + struct netlink_ext_ack *extack) |
|---|
| 2352 | +{ |
|---|
| 2353 | + struct ndtmsg *ndtm; |
|---|
| 2354 | + |
|---|
| 2355 | + if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndtm))) { |
|---|
| 2356 | + NL_SET_ERR_MSG(extack, "Invalid header for neighbor table dump request"); |
|---|
| 2357 | + return -EINVAL; |
|---|
| 2358 | + } |
|---|
| 2359 | + |
|---|
| 2360 | + ndtm = nlmsg_data(nlh); |
|---|
| 2361 | + if (ndtm->ndtm_pad1 || ndtm->ndtm_pad2) { |
|---|
| 2362 | + NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor table dump request"); |
|---|
| 2363 | + return -EINVAL; |
|---|
| 2364 | + } |
|---|
| 2365 | + |
|---|
| 2366 | + if (nlmsg_attrlen(nlh, sizeof(*ndtm))) { |
|---|
| 2367 | + NL_SET_ERR_MSG(extack, "Invalid data after header in neighbor table dump request"); |
|---|
| 2368 | + return -EINVAL; |
|---|
| 2369 | + } |
|---|
| 2370 | + |
|---|
| 2371 | + return 0; |
|---|
| 2372 | +} |
|---|
| 2373 | + |
|---|
| 2172 | 2374 | static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb) |
|---|
| 2173 | 2375 | { |
|---|
| 2376 | + const struct nlmsghdr *nlh = cb->nlh; |
|---|
| 2174 | 2377 | struct net *net = sock_net(skb->sk); |
|---|
| 2175 | 2378 | int family, tidx, nidx = 0; |
|---|
| 2176 | 2379 | int tbl_skip = cb->args[0]; |
|---|
| 2177 | 2380 | int neigh_skip = cb->args[1]; |
|---|
| 2178 | 2381 | struct neigh_table *tbl; |
|---|
| 2179 | 2382 | |
|---|
| 2180 | | - family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family; |
|---|
| 2383 | + if (cb->strict_check) { |
|---|
| 2384 | + int err = neightbl_valid_dump_info(nlh, cb->extack); |
|---|
| 2385 | + |
|---|
| 2386 | + if (err < 0) |
|---|
| 2387 | + return err; |
|---|
| 2388 | + } |
|---|
| 2389 | + |
|---|
| 2390 | + family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family; |
|---|
| 2181 | 2391 | |
|---|
| 2182 | 2392 | for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) { |
|---|
| 2183 | 2393 | struct neigh_parms *p; |
|---|
| .. | .. |
|---|
| 2190 | 2400 | continue; |
|---|
| 2191 | 2401 | |
|---|
| 2192 | 2402 | if (neightbl_fill_info(skb, tbl, NETLINK_CB(cb->skb).portid, |
|---|
| 2193 | | - cb->nlh->nlmsg_seq, RTM_NEWNEIGHTBL, |
|---|
| 2403 | + nlh->nlmsg_seq, RTM_NEWNEIGHTBL, |
|---|
| 2194 | 2404 | NLM_F_MULTI) < 0) |
|---|
| 2195 | 2405 | break; |
|---|
| 2196 | 2406 | |
|---|
| .. | .. |
|---|
| 2205 | 2415 | |
|---|
| 2206 | 2416 | if (neightbl_fill_param_info(skb, tbl, p, |
|---|
| 2207 | 2417 | NETLINK_CB(cb->skb).portid, |
|---|
| 2208 | | - cb->nlh->nlmsg_seq, |
|---|
| 2418 | + nlh->nlmsg_seq, |
|---|
| 2209 | 2419 | RTM_NEWNEIGHTBL, |
|---|
| 2210 | 2420 | NLM_F_MULTI) < 0) |
|---|
| 2211 | 2421 | goto out; |
|---|
| .. | .. |
|---|
| 2267 | 2477 | nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci)) |
|---|
| 2268 | 2478 | goto nla_put_failure; |
|---|
| 2269 | 2479 | |
|---|
| 2480 | + if (neigh->protocol && nla_put_u8(skb, NDA_PROTOCOL, neigh->protocol)) |
|---|
| 2481 | + goto nla_put_failure; |
|---|
| 2482 | + |
|---|
| 2270 | 2483 | nlmsg_end(skb, nlh); |
|---|
| 2271 | 2484 | return 0; |
|---|
| 2272 | 2485 | |
|---|
| .. | .. |
|---|
| 2298 | 2511 | if (nla_put(skb, NDA_DST, tbl->key_len, pn->key)) |
|---|
| 2299 | 2512 | goto nla_put_failure; |
|---|
| 2300 | 2513 | |
|---|
| 2514 | + if (pn->protocol && nla_put_u8(skb, NDA_PROTOCOL, pn->protocol)) |
|---|
| 2515 | + goto nla_put_failure; |
|---|
| 2516 | + |
|---|
| 2301 | 2517 | nlmsg_end(skb, nlh); |
|---|
| 2302 | 2518 | return 0; |
|---|
| 2303 | 2519 | |
|---|
| .. | .. |
|---|
| 2319 | 2535 | if (!master_idx) |
|---|
| 2320 | 2536 | return false; |
|---|
| 2321 | 2537 | |
|---|
| 2322 | | - master = netdev_master_upper_dev_get(dev); |
|---|
| 2538 | + master = dev ? netdev_master_upper_dev_get(dev) : NULL; |
|---|
| 2323 | 2539 | if (!master || master->ifindex != master_idx) |
|---|
| 2324 | 2540 | return true; |
|---|
| 2325 | 2541 | |
|---|
| .. | .. |
|---|
| 2328 | 2544 | |
|---|
| 2329 | 2545 | static bool neigh_ifindex_filtered(struct net_device *dev, int filter_idx) |
|---|
| 2330 | 2546 | { |
|---|
| 2331 | | - if (filter_idx && dev->ifindex != filter_idx) |
|---|
| 2547 | + if (filter_idx && (!dev || dev->ifindex != filter_idx)) |
|---|
| 2332 | 2548 | return true; |
|---|
| 2333 | 2549 | |
|---|
| 2334 | 2550 | return false; |
|---|
| 2335 | 2551 | } |
|---|
| 2336 | 2552 | |
|---|
| 2553 | +struct neigh_dump_filter { |
|---|
| 2554 | + int master_idx; |
|---|
| 2555 | + int dev_idx; |
|---|
| 2556 | +}; |
|---|
| 2557 | + |
|---|
| 2337 | 2558 | static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, |
|---|
| 2338 | | - struct netlink_callback *cb) |
|---|
| 2559 | + struct netlink_callback *cb, |
|---|
| 2560 | + struct neigh_dump_filter *filter) |
|---|
| 2339 | 2561 | { |
|---|
| 2340 | 2562 | struct net *net = sock_net(skb->sk); |
|---|
| 2341 | | - const struct nlmsghdr *nlh = cb->nlh; |
|---|
| 2342 | | - struct nlattr *tb[NDA_MAX + 1]; |
|---|
| 2343 | 2563 | struct neighbour *n; |
|---|
| 2344 | 2564 | int rc, h, s_h = cb->args[1]; |
|---|
| 2345 | 2565 | int idx, s_idx = idx = cb->args[2]; |
|---|
| 2346 | 2566 | struct neigh_hash_table *nht; |
|---|
| 2347 | | - int filter_master_idx = 0, filter_idx = 0; |
|---|
| 2348 | 2567 | unsigned int flags = NLM_F_MULTI; |
|---|
| 2349 | | - int err; |
|---|
| 2350 | 2568 | |
|---|
| 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 | | - } |
|---|
| 2569 | + if (filter->dev_idx || filter->master_idx) |
|---|
| 2570 | + flags |= NLM_F_DUMP_FILTERED; |
|---|
| 2366 | 2571 | |
|---|
| 2367 | 2572 | rcu_read_lock_bh(); |
|---|
| 2368 | 2573 | nht = rcu_dereference_bh(tbl->nht); |
|---|
| .. | .. |
|---|
| 2375 | 2580 | n = rcu_dereference_bh(n->next)) { |
|---|
| 2376 | 2581 | if (idx < s_idx || !net_eq(dev_net(n->dev), net)) |
|---|
| 2377 | 2582 | goto next; |
|---|
| 2378 | | - if (neigh_ifindex_filtered(n->dev, filter_idx) || |
|---|
| 2379 | | - neigh_master_filtered(n->dev, filter_master_idx)) |
|---|
| 2583 | + if (neigh_ifindex_filtered(n->dev, filter->dev_idx) || |
|---|
| 2584 | + neigh_master_filtered(n->dev, filter->master_idx)) |
|---|
| 2380 | 2585 | goto next; |
|---|
| 2381 | 2586 | if (neigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid, |
|---|
| 2382 | 2587 | cb->nlh->nlmsg_seq, |
|---|
| .. | .. |
|---|
| 2398 | 2603 | } |
|---|
| 2399 | 2604 | |
|---|
| 2400 | 2605 | static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb, |
|---|
| 2401 | | - struct netlink_callback *cb) |
|---|
| 2606 | + struct netlink_callback *cb, |
|---|
| 2607 | + struct neigh_dump_filter *filter) |
|---|
| 2402 | 2608 | { |
|---|
| 2403 | 2609 | struct pneigh_entry *n; |
|---|
| 2404 | 2610 | struct net *net = sock_net(skb->sk); |
|---|
| 2405 | 2611 | int rc, h, s_h = cb->args[3]; |
|---|
| 2406 | 2612 | int idx, s_idx = idx = cb->args[4]; |
|---|
| 2613 | + unsigned int flags = NLM_F_MULTI; |
|---|
| 2614 | + |
|---|
| 2615 | + if (filter->dev_idx || filter->master_idx) |
|---|
| 2616 | + flags |= NLM_F_DUMP_FILTERED; |
|---|
| 2407 | 2617 | |
|---|
| 2408 | 2618 | read_lock_bh(&tbl->lock); |
|---|
| 2409 | 2619 | |
|---|
| .. | .. |
|---|
| 2413 | 2623 | for (n = tbl->phash_buckets[h], idx = 0; n; n = n->next) { |
|---|
| 2414 | 2624 | if (idx < s_idx || pneigh_net(n) != net) |
|---|
| 2415 | 2625 | goto next; |
|---|
| 2626 | + if (neigh_ifindex_filtered(n->dev, filter->dev_idx) || |
|---|
| 2627 | + neigh_master_filtered(n->dev, filter->master_idx)) |
|---|
| 2628 | + goto next; |
|---|
| 2416 | 2629 | if (pneigh_fill_info(skb, n, NETLINK_CB(cb->skb).portid, |
|---|
| 2417 | 2630 | cb->nlh->nlmsg_seq, |
|---|
| 2418 | | - RTM_NEWNEIGH, |
|---|
| 2419 | | - NLM_F_MULTI, tbl) < 0) { |
|---|
| 2631 | + RTM_NEWNEIGH, flags, tbl) < 0) { |
|---|
| 2420 | 2632 | read_unlock_bh(&tbl->lock); |
|---|
| 2421 | 2633 | rc = -1; |
|---|
| 2422 | 2634 | goto out; |
|---|
| .. | .. |
|---|
| 2435 | 2647 | |
|---|
| 2436 | 2648 | } |
|---|
| 2437 | 2649 | |
|---|
| 2650 | +static int neigh_valid_dump_req(const struct nlmsghdr *nlh, |
|---|
| 2651 | + bool strict_check, |
|---|
| 2652 | + struct neigh_dump_filter *filter, |
|---|
| 2653 | + struct netlink_ext_ack *extack) |
|---|
| 2654 | +{ |
|---|
| 2655 | + struct nlattr *tb[NDA_MAX + 1]; |
|---|
| 2656 | + int err, i; |
|---|
| 2657 | + |
|---|
| 2658 | + if (strict_check) { |
|---|
| 2659 | + struct ndmsg *ndm; |
|---|
| 2660 | + |
|---|
| 2661 | + if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) { |
|---|
| 2662 | + NL_SET_ERR_MSG(extack, "Invalid header for neighbor dump request"); |
|---|
| 2663 | + return -EINVAL; |
|---|
| 2664 | + } |
|---|
| 2665 | + |
|---|
| 2666 | + ndm = nlmsg_data(nlh); |
|---|
| 2667 | + if (ndm->ndm_pad1 || ndm->ndm_pad2 || ndm->ndm_ifindex || |
|---|
| 2668 | + ndm->ndm_state || ndm->ndm_type) { |
|---|
| 2669 | + NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor dump request"); |
|---|
| 2670 | + return -EINVAL; |
|---|
| 2671 | + } |
|---|
| 2672 | + |
|---|
| 2673 | + if (ndm->ndm_flags & ~NTF_PROXY) { |
|---|
| 2674 | + NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor dump request"); |
|---|
| 2675 | + return -EINVAL; |
|---|
| 2676 | + } |
|---|
| 2677 | + |
|---|
| 2678 | + err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct ndmsg), |
|---|
| 2679 | + tb, NDA_MAX, nda_policy, |
|---|
| 2680 | + extack); |
|---|
| 2681 | + } else { |
|---|
| 2682 | + err = nlmsg_parse_deprecated(nlh, sizeof(struct ndmsg), tb, |
|---|
| 2683 | + NDA_MAX, nda_policy, extack); |
|---|
| 2684 | + } |
|---|
| 2685 | + if (err < 0) |
|---|
| 2686 | + return err; |
|---|
| 2687 | + |
|---|
| 2688 | + for (i = 0; i <= NDA_MAX; ++i) { |
|---|
| 2689 | + if (!tb[i]) |
|---|
| 2690 | + continue; |
|---|
| 2691 | + |
|---|
| 2692 | + /* all new attributes should require strict_check */ |
|---|
| 2693 | + switch (i) { |
|---|
| 2694 | + case NDA_IFINDEX: |
|---|
| 2695 | + filter->dev_idx = nla_get_u32(tb[i]); |
|---|
| 2696 | + break; |
|---|
| 2697 | + case NDA_MASTER: |
|---|
| 2698 | + filter->master_idx = nla_get_u32(tb[i]); |
|---|
| 2699 | + break; |
|---|
| 2700 | + default: |
|---|
| 2701 | + if (strict_check) { |
|---|
| 2702 | + NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor dump request"); |
|---|
| 2703 | + return -EINVAL; |
|---|
| 2704 | + } |
|---|
| 2705 | + } |
|---|
| 2706 | + } |
|---|
| 2707 | + |
|---|
| 2708 | + return 0; |
|---|
| 2709 | +} |
|---|
| 2710 | + |
|---|
| 2438 | 2711 | static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb) |
|---|
| 2439 | 2712 | { |
|---|
| 2713 | + const struct nlmsghdr *nlh = cb->nlh; |
|---|
| 2714 | + struct neigh_dump_filter filter = {}; |
|---|
| 2440 | 2715 | struct neigh_table *tbl; |
|---|
| 2441 | 2716 | int t, family, s_t; |
|---|
| 2442 | 2717 | int proxy = 0; |
|---|
| 2443 | 2718 | int err; |
|---|
| 2444 | 2719 | |
|---|
| 2445 | | - family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family; |
|---|
| 2720 | + family = ((struct rtgenmsg *)nlmsg_data(nlh))->rtgen_family; |
|---|
| 2446 | 2721 | |
|---|
| 2447 | 2722 | /* check for full ndmsg structure presence, family member is |
|---|
| 2448 | 2723 | * the same for both structures |
|---|
| 2449 | 2724 | */ |
|---|
| 2450 | | - if (nlmsg_len(cb->nlh) >= sizeof(struct ndmsg) && |
|---|
| 2451 | | - ((struct ndmsg *) nlmsg_data(cb->nlh))->ndm_flags == NTF_PROXY) |
|---|
| 2725 | + if (nlmsg_len(nlh) >= sizeof(struct ndmsg) && |
|---|
| 2726 | + ((struct ndmsg *)nlmsg_data(nlh))->ndm_flags == NTF_PROXY) |
|---|
| 2452 | 2727 | proxy = 1; |
|---|
| 2728 | + |
|---|
| 2729 | + err = neigh_valid_dump_req(nlh, cb->strict_check, &filter, cb->extack); |
|---|
| 2730 | + if (err < 0 && cb->strict_check) |
|---|
| 2731 | + return err; |
|---|
| 2453 | 2732 | |
|---|
| 2454 | 2733 | s_t = cb->args[0]; |
|---|
| 2455 | 2734 | |
|---|
| .. | .. |
|---|
| 2464 | 2743 | memset(&cb->args[1], 0, sizeof(cb->args) - |
|---|
| 2465 | 2744 | sizeof(cb->args[0])); |
|---|
| 2466 | 2745 | if (proxy) |
|---|
| 2467 | | - err = pneigh_dump_table(tbl, skb, cb); |
|---|
| 2746 | + err = pneigh_dump_table(tbl, skb, cb, &filter); |
|---|
| 2468 | 2747 | else |
|---|
| 2469 | | - err = neigh_dump_table(tbl, skb, cb); |
|---|
| 2748 | + err = neigh_dump_table(tbl, skb, cb, &filter); |
|---|
| 2470 | 2749 | if (err < 0) |
|---|
| 2471 | 2750 | break; |
|---|
| 2472 | 2751 | } |
|---|
| 2473 | 2752 | |
|---|
| 2474 | 2753 | cb->args[0] = t; |
|---|
| 2475 | 2754 | return skb->len; |
|---|
| 2755 | +} |
|---|
| 2756 | + |
|---|
| 2757 | +static int neigh_valid_get_req(const struct nlmsghdr *nlh, |
|---|
| 2758 | + struct neigh_table **tbl, |
|---|
| 2759 | + void **dst, int *dev_idx, u8 *ndm_flags, |
|---|
| 2760 | + struct netlink_ext_ack *extack) |
|---|
| 2761 | +{ |
|---|
| 2762 | + struct nlattr *tb[NDA_MAX + 1]; |
|---|
| 2763 | + struct ndmsg *ndm; |
|---|
| 2764 | + int err, i; |
|---|
| 2765 | + |
|---|
| 2766 | + if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ndm))) { |
|---|
| 2767 | + NL_SET_ERR_MSG(extack, "Invalid header for neighbor get request"); |
|---|
| 2768 | + return -EINVAL; |
|---|
| 2769 | + } |
|---|
| 2770 | + |
|---|
| 2771 | + ndm = nlmsg_data(nlh); |
|---|
| 2772 | + if (ndm->ndm_pad1 || ndm->ndm_pad2 || ndm->ndm_state || |
|---|
| 2773 | + ndm->ndm_type) { |
|---|
| 2774 | + NL_SET_ERR_MSG(extack, "Invalid values in header for neighbor get request"); |
|---|
| 2775 | + return -EINVAL; |
|---|
| 2776 | + } |
|---|
| 2777 | + |
|---|
| 2778 | + if (ndm->ndm_flags & ~NTF_PROXY) { |
|---|
| 2779 | + NL_SET_ERR_MSG(extack, "Invalid flags in header for neighbor get request"); |
|---|
| 2780 | + return -EINVAL; |
|---|
| 2781 | + } |
|---|
| 2782 | + |
|---|
| 2783 | + err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct ndmsg), tb, |
|---|
| 2784 | + NDA_MAX, nda_policy, extack); |
|---|
| 2785 | + if (err < 0) |
|---|
| 2786 | + return err; |
|---|
| 2787 | + |
|---|
| 2788 | + *ndm_flags = ndm->ndm_flags; |
|---|
| 2789 | + *dev_idx = ndm->ndm_ifindex; |
|---|
| 2790 | + *tbl = neigh_find_table(ndm->ndm_family); |
|---|
| 2791 | + if (*tbl == NULL) { |
|---|
| 2792 | + NL_SET_ERR_MSG(extack, "Unsupported family in header for neighbor get request"); |
|---|
| 2793 | + return -EAFNOSUPPORT; |
|---|
| 2794 | + } |
|---|
| 2795 | + |
|---|
| 2796 | + for (i = 0; i <= NDA_MAX; ++i) { |
|---|
| 2797 | + if (!tb[i]) |
|---|
| 2798 | + continue; |
|---|
| 2799 | + |
|---|
| 2800 | + switch (i) { |
|---|
| 2801 | + case NDA_DST: |
|---|
| 2802 | + if (nla_len(tb[i]) != (int)(*tbl)->key_len) { |
|---|
| 2803 | + NL_SET_ERR_MSG(extack, "Invalid network address in neighbor get request"); |
|---|
| 2804 | + return -EINVAL; |
|---|
| 2805 | + } |
|---|
| 2806 | + *dst = nla_data(tb[i]); |
|---|
| 2807 | + break; |
|---|
| 2808 | + default: |
|---|
| 2809 | + NL_SET_ERR_MSG(extack, "Unsupported attribute in neighbor get request"); |
|---|
| 2810 | + return -EINVAL; |
|---|
| 2811 | + } |
|---|
| 2812 | + } |
|---|
| 2813 | + |
|---|
| 2814 | + return 0; |
|---|
| 2815 | +} |
|---|
| 2816 | + |
|---|
| 2817 | +static inline size_t neigh_nlmsg_size(void) |
|---|
| 2818 | +{ |
|---|
| 2819 | + return NLMSG_ALIGN(sizeof(struct ndmsg)) |
|---|
| 2820 | + + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */ |
|---|
| 2821 | + + nla_total_size(MAX_ADDR_LEN) /* NDA_LLADDR */ |
|---|
| 2822 | + + nla_total_size(sizeof(struct nda_cacheinfo)) |
|---|
| 2823 | + + nla_total_size(4) /* NDA_PROBES */ |
|---|
| 2824 | + + nla_total_size(1); /* NDA_PROTOCOL */ |
|---|
| 2825 | +} |
|---|
| 2826 | + |
|---|
| 2827 | +static int neigh_get_reply(struct net *net, struct neighbour *neigh, |
|---|
| 2828 | + u32 pid, u32 seq) |
|---|
| 2829 | +{ |
|---|
| 2830 | + struct sk_buff *skb; |
|---|
| 2831 | + int err = 0; |
|---|
| 2832 | + |
|---|
| 2833 | + skb = nlmsg_new(neigh_nlmsg_size(), GFP_KERNEL); |
|---|
| 2834 | + if (!skb) |
|---|
| 2835 | + return -ENOBUFS; |
|---|
| 2836 | + |
|---|
| 2837 | + err = neigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0); |
|---|
| 2838 | + if (err) { |
|---|
| 2839 | + kfree_skb(skb); |
|---|
| 2840 | + goto errout; |
|---|
| 2841 | + } |
|---|
| 2842 | + |
|---|
| 2843 | + err = rtnl_unicast(skb, net, pid); |
|---|
| 2844 | +errout: |
|---|
| 2845 | + return err; |
|---|
| 2846 | +} |
|---|
| 2847 | + |
|---|
| 2848 | +static inline size_t pneigh_nlmsg_size(void) |
|---|
| 2849 | +{ |
|---|
| 2850 | + return NLMSG_ALIGN(sizeof(struct ndmsg)) |
|---|
| 2851 | + + nla_total_size(MAX_ADDR_LEN) /* NDA_DST */ |
|---|
| 2852 | + + nla_total_size(1); /* NDA_PROTOCOL */ |
|---|
| 2853 | +} |
|---|
| 2854 | + |
|---|
| 2855 | +static int pneigh_get_reply(struct net *net, struct pneigh_entry *neigh, |
|---|
| 2856 | + u32 pid, u32 seq, struct neigh_table *tbl) |
|---|
| 2857 | +{ |
|---|
| 2858 | + struct sk_buff *skb; |
|---|
| 2859 | + int err = 0; |
|---|
| 2860 | + |
|---|
| 2861 | + skb = nlmsg_new(pneigh_nlmsg_size(), GFP_KERNEL); |
|---|
| 2862 | + if (!skb) |
|---|
| 2863 | + return -ENOBUFS; |
|---|
| 2864 | + |
|---|
| 2865 | + err = pneigh_fill_info(skb, neigh, pid, seq, RTM_NEWNEIGH, 0, tbl); |
|---|
| 2866 | + if (err) { |
|---|
| 2867 | + kfree_skb(skb); |
|---|
| 2868 | + goto errout; |
|---|
| 2869 | + } |
|---|
| 2870 | + |
|---|
| 2871 | + err = rtnl_unicast(skb, net, pid); |
|---|
| 2872 | +errout: |
|---|
| 2873 | + return err; |
|---|
| 2874 | +} |
|---|
| 2875 | + |
|---|
| 2876 | +static int neigh_get(struct sk_buff *in_skb, struct nlmsghdr *nlh, |
|---|
| 2877 | + struct netlink_ext_ack *extack) |
|---|
| 2878 | +{ |
|---|
| 2879 | + struct net *net = sock_net(in_skb->sk); |
|---|
| 2880 | + struct net_device *dev = NULL; |
|---|
| 2881 | + struct neigh_table *tbl = NULL; |
|---|
| 2882 | + struct neighbour *neigh; |
|---|
| 2883 | + void *dst = NULL; |
|---|
| 2884 | + u8 ndm_flags = 0; |
|---|
| 2885 | + int dev_idx = 0; |
|---|
| 2886 | + int err; |
|---|
| 2887 | + |
|---|
| 2888 | + err = neigh_valid_get_req(nlh, &tbl, &dst, &dev_idx, &ndm_flags, |
|---|
| 2889 | + extack); |
|---|
| 2890 | + if (err < 0) |
|---|
| 2891 | + return err; |
|---|
| 2892 | + |
|---|
| 2893 | + if (dev_idx) { |
|---|
| 2894 | + dev = __dev_get_by_index(net, dev_idx); |
|---|
| 2895 | + if (!dev) { |
|---|
| 2896 | + NL_SET_ERR_MSG(extack, "Unknown device ifindex"); |
|---|
| 2897 | + return -ENODEV; |
|---|
| 2898 | + } |
|---|
| 2899 | + } |
|---|
| 2900 | + |
|---|
| 2901 | + if (!dst) { |
|---|
| 2902 | + NL_SET_ERR_MSG(extack, "Network address not specified"); |
|---|
| 2903 | + return -EINVAL; |
|---|
| 2904 | + } |
|---|
| 2905 | + |
|---|
| 2906 | + if (ndm_flags & NTF_PROXY) { |
|---|
| 2907 | + struct pneigh_entry *pn; |
|---|
| 2908 | + |
|---|
| 2909 | + pn = pneigh_lookup(tbl, net, dst, dev, 0); |
|---|
| 2910 | + if (!pn) { |
|---|
| 2911 | + NL_SET_ERR_MSG(extack, "Proxy neighbour entry not found"); |
|---|
| 2912 | + return -ENOENT; |
|---|
| 2913 | + } |
|---|
| 2914 | + return pneigh_get_reply(net, pn, NETLINK_CB(in_skb).portid, |
|---|
| 2915 | + nlh->nlmsg_seq, tbl); |
|---|
| 2916 | + } |
|---|
| 2917 | + |
|---|
| 2918 | + if (!dev) { |
|---|
| 2919 | + NL_SET_ERR_MSG(extack, "No device specified"); |
|---|
| 2920 | + return -EINVAL; |
|---|
| 2921 | + } |
|---|
| 2922 | + |
|---|
| 2923 | + neigh = neigh_lookup(tbl, dst, dev); |
|---|
| 2924 | + if (!neigh) { |
|---|
| 2925 | + NL_SET_ERR_MSG(extack, "Neighbour entry not found"); |
|---|
| 2926 | + return -ENOENT; |
|---|
| 2927 | + } |
|---|
| 2928 | + |
|---|
| 2929 | + err = neigh_get_reply(net, neigh, NETLINK_CB(in_skb).portid, |
|---|
| 2930 | + nlh->nlmsg_seq); |
|---|
| 2931 | + |
|---|
| 2932 | + neigh_release(neigh); |
|---|
| 2933 | + |
|---|
| 2934 | + return err; |
|---|
| 2476 | 2935 | } |
|---|
| 2477 | 2936 | |
|---|
| 2478 | 2937 | void neigh_for_each(struct neigh_table *tbl, void (*cb)(struct neighbour *, void *), void *cookie) |
|---|
| .. | .. |
|---|
| 2521 | 2980 | rcu_assign_pointer(*np, |
|---|
| 2522 | 2981 | rcu_dereference_protected(n->next, |
|---|
| 2523 | 2982 | lockdep_is_held(&tbl->lock))); |
|---|
| 2524 | | - n->dead = 1; |
|---|
| 2983 | + neigh_mark_dead(n); |
|---|
| 2525 | 2984 | } else |
|---|
| 2526 | 2985 | np = &n->next; |
|---|
| 2527 | 2986 | write_unlock(&n->lock); |
|---|
| .. | .. |
|---|
| 2584 | 3043 | struct net *net = seq_file_net(seq); |
|---|
| 2585 | 3044 | struct neigh_hash_table *nht = state->nht; |
|---|
| 2586 | 3045 | struct neighbour *n = NULL; |
|---|
| 2587 | | - int bucket = state->bucket; |
|---|
| 3046 | + int bucket; |
|---|
| 2588 | 3047 | |
|---|
| 2589 | 3048 | state->flags &= ~NEIGH_SEQ_IS_PNEIGH; |
|---|
| 2590 | 3049 | for (bucket = 0; bucket < (1 << nht->hash_shift); bucket++) { |
|---|
| .. | .. |
|---|
| 2896 | 3355 | }; |
|---|
| 2897 | 3356 | #endif /* CONFIG_PROC_FS */ |
|---|
| 2898 | 3357 | |
|---|
| 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 | 3358 | static void __neigh_notify(struct neighbour *n, int type, int flags, |
|---|
| 2909 | 3359 | u32 pid) |
|---|
| 2910 | 3360 | { |
|---|
| .. | .. |
|---|
| 2937 | 3387 | EXPORT_SYMBOL(neigh_app_ns); |
|---|
| 2938 | 3388 | |
|---|
| 2939 | 3389 | #ifdef CONFIG_SYSCTL |
|---|
| 2940 | | -static int zero; |
|---|
| 2941 | | -static int int_max = INT_MAX; |
|---|
| 2942 | 3390 | static int unres_qlen_max = INT_MAX / SKB_TRUESIZE(ETH_FRAME_LEN); |
|---|
| 2943 | 3391 | |
|---|
| 2944 | 3392 | static int proc_unres_qlen(struct ctl_table *ctl, int write, |
|---|
| 2945 | | - void __user *buffer, size_t *lenp, loff_t *ppos) |
|---|
| 3393 | + void *buffer, size_t *lenp, loff_t *ppos) |
|---|
| 2946 | 3394 | { |
|---|
| 2947 | 3395 | int size, ret; |
|---|
| 2948 | 3396 | struct ctl_table tmp = *ctl; |
|---|
| 2949 | 3397 | |
|---|
| 2950 | | - tmp.extra1 = &zero; |
|---|
| 3398 | + tmp.extra1 = SYSCTL_ZERO; |
|---|
| 2951 | 3399 | tmp.extra2 = &unres_qlen_max; |
|---|
| 2952 | 3400 | tmp.data = &size; |
|---|
| 2953 | 3401 | |
|---|
| .. | .. |
|---|
| 3006 | 3454 | } |
|---|
| 3007 | 3455 | |
|---|
| 3008 | 3456 | static int neigh_proc_dointvec_zero_intmax(struct ctl_table *ctl, int write, |
|---|
| 3009 | | - void __user *buffer, |
|---|
| 3010 | | - size_t *lenp, loff_t *ppos) |
|---|
| 3457 | + void *buffer, size_t *lenp, |
|---|
| 3458 | + loff_t *ppos) |
|---|
| 3011 | 3459 | { |
|---|
| 3012 | 3460 | struct ctl_table tmp = *ctl; |
|---|
| 3013 | 3461 | int ret; |
|---|
| 3014 | 3462 | |
|---|
| 3015 | | - tmp.extra1 = &zero; |
|---|
| 3016 | | - tmp.extra2 = &int_max; |
|---|
| 3463 | + tmp.extra1 = SYSCTL_ZERO; |
|---|
| 3464 | + tmp.extra2 = SYSCTL_INT_MAX; |
|---|
| 3017 | 3465 | |
|---|
| 3018 | 3466 | ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); |
|---|
| 3019 | 3467 | neigh_proc_update(ctl, write); |
|---|
| 3020 | 3468 | return ret; |
|---|
| 3021 | 3469 | } |
|---|
| 3022 | 3470 | |
|---|
| 3023 | | -int neigh_proc_dointvec(struct ctl_table *ctl, int write, |
|---|
| 3024 | | - void __user *buffer, size_t *lenp, loff_t *ppos) |
|---|
| 3471 | +int neigh_proc_dointvec(struct ctl_table *ctl, int write, void *buffer, |
|---|
| 3472 | + size_t *lenp, loff_t *ppos) |
|---|
| 3025 | 3473 | { |
|---|
| 3026 | 3474 | int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); |
|---|
| 3027 | 3475 | |
|---|
| .. | .. |
|---|
| 3030 | 3478 | } |
|---|
| 3031 | 3479 | EXPORT_SYMBOL(neigh_proc_dointvec); |
|---|
| 3032 | 3480 | |
|---|
| 3033 | | -int neigh_proc_dointvec_jiffies(struct ctl_table *ctl, int write, |
|---|
| 3034 | | - void __user *buffer, |
|---|
| 3481 | +int neigh_proc_dointvec_jiffies(struct ctl_table *ctl, int write, void *buffer, |
|---|
| 3035 | 3482 | size_t *lenp, loff_t *ppos) |
|---|
| 3036 | 3483 | { |
|---|
| 3037 | 3484 | int ret = proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos); |
|---|
| .. | .. |
|---|
| 3042 | 3489 | EXPORT_SYMBOL(neigh_proc_dointvec_jiffies); |
|---|
| 3043 | 3490 | |
|---|
| 3044 | 3491 | static int neigh_proc_dointvec_userhz_jiffies(struct ctl_table *ctl, int write, |
|---|
| 3045 | | - void __user *buffer, |
|---|
| 3046 | | - size_t *lenp, loff_t *ppos) |
|---|
| 3492 | + void *buffer, size_t *lenp, |
|---|
| 3493 | + loff_t *ppos) |
|---|
| 3047 | 3494 | { |
|---|
| 3048 | 3495 | int ret = proc_dointvec_userhz_jiffies(ctl, write, buffer, lenp, ppos); |
|---|
| 3049 | 3496 | |
|---|
| .. | .. |
|---|
| 3052 | 3499 | } |
|---|
| 3053 | 3500 | |
|---|
| 3054 | 3501 | int neigh_proc_dointvec_ms_jiffies(struct ctl_table *ctl, int write, |
|---|
| 3055 | | - void __user *buffer, |
|---|
| 3056 | | - size_t *lenp, loff_t *ppos) |
|---|
| 3502 | + void *buffer, size_t *lenp, loff_t *ppos) |
|---|
| 3057 | 3503 | { |
|---|
| 3058 | 3504 | int ret = proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos); |
|---|
| 3059 | 3505 | |
|---|
| .. | .. |
|---|
| 3063 | 3509 | EXPORT_SYMBOL(neigh_proc_dointvec_ms_jiffies); |
|---|
| 3064 | 3510 | |
|---|
| 3065 | 3511 | static int neigh_proc_dointvec_unres_qlen(struct ctl_table *ctl, int write, |
|---|
| 3066 | | - void __user *buffer, |
|---|
| 3067 | | - size_t *lenp, loff_t *ppos) |
|---|
| 3512 | + void *buffer, size_t *lenp, |
|---|
| 3513 | + loff_t *ppos) |
|---|
| 3068 | 3514 | { |
|---|
| 3069 | 3515 | int ret = proc_unres_qlen(ctl, write, buffer, lenp, ppos); |
|---|
| 3070 | 3516 | |
|---|
| .. | .. |
|---|
| 3073 | 3519 | } |
|---|
| 3074 | 3520 | |
|---|
| 3075 | 3521 | static int neigh_proc_base_reachable_time(struct ctl_table *ctl, int write, |
|---|
| 3076 | | - void __user *buffer, |
|---|
| 3077 | | - size_t *lenp, loff_t *ppos) |
|---|
| 3522 | + void *buffer, size_t *lenp, |
|---|
| 3523 | + loff_t *ppos) |
|---|
| 3078 | 3524 | { |
|---|
| 3079 | 3525 | struct neigh_parms *p = ctl->extra2; |
|---|
| 3080 | 3526 | int ret; |
|---|
| .. | .. |
|---|
| 3118 | 3564 | #define NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(attr, name) \ |
|---|
| 3119 | 3565 | NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_userhz_jiffies) |
|---|
| 3120 | 3566 | |
|---|
| 3121 | | -#define NEIGH_SYSCTL_MS_JIFFIES_ENTRY(attr, name) \ |
|---|
| 3122 | | - NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_ms_jiffies) |
|---|
| 3123 | | - |
|---|
| 3124 | 3567 | #define NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(attr, data_attr, name) \ |
|---|
| 3125 | 3568 | NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_ms_jiffies) |
|---|
| 3126 | 3569 | |
|---|
| .. | .. |
|---|
| 3158 | 3601 | .procname = "gc_thresh1", |
|---|
| 3159 | 3602 | .maxlen = sizeof(int), |
|---|
| 3160 | 3603 | .mode = 0644, |
|---|
| 3161 | | - .extra1 = &zero, |
|---|
| 3162 | | - .extra2 = &int_max, |
|---|
| 3604 | + .extra1 = SYSCTL_ZERO, |
|---|
| 3605 | + .extra2 = SYSCTL_INT_MAX, |
|---|
| 3163 | 3606 | .proc_handler = proc_dointvec_minmax, |
|---|
| 3164 | 3607 | }, |
|---|
| 3165 | 3608 | [NEIGH_VAR_GC_THRESH2] = { |
|---|
| 3166 | 3609 | .procname = "gc_thresh2", |
|---|
| 3167 | 3610 | .maxlen = sizeof(int), |
|---|
| 3168 | 3611 | .mode = 0644, |
|---|
| 3169 | | - .extra1 = &zero, |
|---|
| 3170 | | - .extra2 = &int_max, |
|---|
| 3612 | + .extra1 = SYSCTL_ZERO, |
|---|
| 3613 | + .extra2 = SYSCTL_INT_MAX, |
|---|
| 3171 | 3614 | .proc_handler = proc_dointvec_minmax, |
|---|
| 3172 | 3615 | }, |
|---|
| 3173 | 3616 | [NEIGH_VAR_GC_THRESH3] = { |
|---|
| 3174 | 3617 | .procname = "gc_thresh3", |
|---|
| 3175 | 3618 | .maxlen = sizeof(int), |
|---|
| 3176 | 3619 | .mode = 0644, |
|---|
| 3177 | | - .extra1 = &zero, |
|---|
| 3178 | | - .extra2 = &int_max, |
|---|
| 3620 | + .extra1 = SYSCTL_ZERO, |
|---|
| 3621 | + .extra2 = SYSCTL_INT_MAX, |
|---|
| 3179 | 3622 | .proc_handler = proc_dointvec_minmax, |
|---|
| 3180 | 3623 | }, |
|---|
| 3181 | 3624 | {}, |
|---|
| .. | .. |
|---|
| 3288 | 3731 | { |
|---|
| 3289 | 3732 | rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL, 0); |
|---|
| 3290 | 3733 | rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL, 0); |
|---|
| 3291 | | - rtnl_register(PF_UNSPEC, RTM_GETNEIGH, NULL, neigh_dump_info, 0); |
|---|
| 3734 | + rtnl_register(PF_UNSPEC, RTM_GETNEIGH, neigh_get, neigh_dump_info, 0); |
|---|
| 3292 | 3735 | |
|---|
| 3293 | 3736 | rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info, |
|---|
| 3294 | 3737 | 0); |
|---|