.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Linux INET6 implementation |
---|
3 | 4 | * Forwarding Information Database |
---|
4 | 5 | * |
---|
5 | 6 | * Authors: |
---|
6 | 7 | * Pedro Roque <roque@di.fc.ul.pt> |
---|
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 | * Changes: |
---|
14 | 10 | * Yuji SEKIYA @USAGI: Support default route on router node; |
---|
.. | .. |
---|
29 | 25 | #include <linux/list.h> |
---|
30 | 26 | #include <linux/slab.h> |
---|
31 | 27 | |
---|
| 28 | +#include <net/ip.h> |
---|
32 | 29 | #include <net/ipv6.h> |
---|
33 | 30 | #include <net/ndisc.h> |
---|
34 | 31 | #include <net/addrconf.h> |
---|
.. | .. |
---|
46 | 43 | int (*func)(struct fib6_info *, void *arg); |
---|
47 | 44 | int sernum; |
---|
48 | 45 | void *arg; |
---|
| 46 | + bool skip_notify; |
---|
49 | 47 | }; |
---|
50 | 48 | |
---|
51 | 49 | #ifdef CONFIG_IPV6_SUBTREES |
---|
.. | .. |
---|
145 | 143 | addr[fn_bit >> 5]; |
---|
146 | 144 | } |
---|
147 | 145 | |
---|
148 | | -struct fib6_info *fib6_info_alloc(gfp_t gfp_flags) |
---|
| 146 | +struct fib6_info *fib6_info_alloc(gfp_t gfp_flags, bool with_fib6_nh) |
---|
149 | 147 | { |
---|
150 | 148 | struct fib6_info *f6i; |
---|
| 149 | + size_t sz = sizeof(*f6i); |
---|
151 | 150 | |
---|
152 | | - f6i = kzalloc(sizeof(*f6i), gfp_flags); |
---|
| 151 | + if (with_fib6_nh) |
---|
| 152 | + sz += sizeof(struct fib6_nh); |
---|
| 153 | + |
---|
| 154 | + f6i = kzalloc(sz, gfp_flags); |
---|
153 | 155 | if (!f6i) |
---|
154 | 156 | return NULL; |
---|
155 | 157 | |
---|
156 | | - f6i->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, gfp_flags); |
---|
157 | | - if (!f6i->rt6i_pcpu) { |
---|
158 | | - kfree(f6i); |
---|
159 | | - return NULL; |
---|
160 | | - } |
---|
161 | | - |
---|
| 158 | + /* fib6_siblings is a union with nh_list, so this initializes both */ |
---|
162 | 159 | INIT_LIST_HEAD(&f6i->fib6_siblings); |
---|
163 | | - f6i->fib6_metrics = (struct dst_metrics *)&dst_default_metrics; |
---|
164 | | - |
---|
165 | | - atomic_inc(&f6i->fib6_ref); |
---|
| 160 | + refcount_set(&f6i->fib6_ref, 1); |
---|
166 | 161 | |
---|
167 | 162 | return f6i; |
---|
168 | 163 | } |
---|
.. | .. |
---|
170 | 165 | void fib6_info_destroy_rcu(struct rcu_head *head) |
---|
171 | 166 | { |
---|
172 | 167 | struct fib6_info *f6i = container_of(head, struct fib6_info, rcu); |
---|
173 | | - struct rt6_exception_bucket *bucket; |
---|
174 | | - struct dst_metrics *m; |
---|
175 | 168 | |
---|
176 | 169 | WARN_ON(f6i->fib6_node); |
---|
177 | 170 | |
---|
178 | | - bucket = rcu_dereference_protected(f6i->rt6i_exception_bucket, 1); |
---|
179 | | - if (bucket) { |
---|
180 | | - f6i->rt6i_exception_bucket = NULL; |
---|
181 | | - kfree(bucket); |
---|
182 | | - } |
---|
| 171 | + if (f6i->nh) |
---|
| 172 | + nexthop_put(f6i->nh); |
---|
| 173 | + else |
---|
| 174 | + fib6_nh_release(f6i->fib6_nh); |
---|
183 | 175 | |
---|
184 | | - if (f6i->rt6i_pcpu) { |
---|
185 | | - int cpu; |
---|
186 | | - |
---|
187 | | - for_each_possible_cpu(cpu) { |
---|
188 | | - struct rt6_info **ppcpu_rt; |
---|
189 | | - struct rt6_info *pcpu_rt; |
---|
190 | | - |
---|
191 | | - ppcpu_rt = per_cpu_ptr(f6i->rt6i_pcpu, cpu); |
---|
192 | | - pcpu_rt = *ppcpu_rt; |
---|
193 | | - if (pcpu_rt) { |
---|
194 | | - dst_dev_put(&pcpu_rt->dst); |
---|
195 | | - dst_release(&pcpu_rt->dst); |
---|
196 | | - *ppcpu_rt = NULL; |
---|
197 | | - } |
---|
198 | | - } |
---|
199 | | - |
---|
200 | | - free_percpu(f6i->rt6i_pcpu); |
---|
201 | | - } |
---|
202 | | - |
---|
203 | | - lwtstate_put(f6i->fib6_nh.nh_lwtstate); |
---|
204 | | - |
---|
205 | | - if (f6i->fib6_nh.nh_dev) |
---|
206 | | - dev_put(f6i->fib6_nh.nh_dev); |
---|
207 | | - |
---|
208 | | - m = f6i->fib6_metrics; |
---|
209 | | - if (m != &dst_default_metrics && refcount_dec_and_test(&m->refcnt)) |
---|
210 | | - kfree(m); |
---|
211 | | - |
---|
| 176 | + ip_fib_metrics_put(f6i->fib6_metrics); |
---|
212 | 177 | kfree(f6i); |
---|
213 | 178 | } |
---|
214 | 179 | EXPORT_SYMBOL_GPL(fib6_info_destroy_rcu); |
---|
.. | .. |
---|
349 | 314 | { |
---|
350 | 315 | struct rt6_info *rt; |
---|
351 | 316 | |
---|
352 | | - rt = lookup(net, net->ipv6.fib6_main_tbl, fl6, skb, flags); |
---|
| 317 | + rt = pol_lookup_func(lookup, |
---|
| 318 | + net, net->ipv6.fib6_main_tbl, fl6, skb, flags); |
---|
353 | 319 | if (rt->dst.error == -EAGAIN) { |
---|
354 | | - ip6_rt_put(rt); |
---|
| 320 | + ip6_rt_put_flags(rt, flags); |
---|
355 | 321 | rt = net->ipv6.ip6_null_entry; |
---|
356 | | - dst_hold(&rt->dst); |
---|
| 322 | + if (!(flags & RT6_LOOKUP_F_DST_NOREF)) |
---|
| 323 | + dst_hold(&rt->dst); |
---|
357 | 324 | } |
---|
358 | 325 | |
---|
359 | 326 | return &rt->dst; |
---|
360 | 327 | } |
---|
361 | 328 | |
---|
362 | 329 | /* called with rcu lock held; no reference taken on fib6_info */ |
---|
363 | | -struct fib6_info *fib6_lookup(struct net *net, int oif, struct flowi6 *fl6, |
---|
364 | | - int flags) |
---|
| 330 | +int fib6_lookup(struct net *net, int oif, struct flowi6 *fl6, |
---|
| 331 | + struct fib6_result *res, int flags) |
---|
365 | 332 | { |
---|
366 | | - return fib6_table_lookup(net, net->ipv6.fib6_main_tbl, oif, fl6, flags); |
---|
| 333 | + return fib6_table_lookup(net, net->ipv6.fib6_main_tbl, oif, fl6, |
---|
| 334 | + res, flags); |
---|
367 | 335 | } |
---|
368 | 336 | |
---|
369 | 337 | static void __net_init fib6_tables_init(struct net *net) |
---|
.. | .. |
---|
390 | 358 | return fib_seq; |
---|
391 | 359 | } |
---|
392 | 360 | |
---|
393 | | -static int call_fib6_entry_notifier(struct notifier_block *nb, struct net *net, |
---|
| 361 | +static int call_fib6_entry_notifier(struct notifier_block *nb, |
---|
394 | 362 | enum fib_event_type event_type, |
---|
395 | | - struct fib6_info *rt) |
---|
| 363 | + struct fib6_info *rt, |
---|
| 364 | + struct netlink_ext_ack *extack) |
---|
396 | 365 | { |
---|
397 | 366 | struct fib6_entry_notifier_info info = { |
---|
| 367 | + .info.extack = extack, |
---|
398 | 368 | .rt = rt, |
---|
399 | 369 | }; |
---|
400 | 370 | |
---|
401 | | - return call_fib6_notifier(nb, net, event_type, &info.info); |
---|
| 371 | + return call_fib6_notifier(nb, event_type, &info.info); |
---|
402 | 372 | } |
---|
403 | 373 | |
---|
404 | | -static int call_fib6_entry_notifiers(struct net *net, |
---|
405 | | - enum fib_event_type event_type, |
---|
406 | | - struct fib6_info *rt, |
---|
407 | | - struct netlink_ext_ack *extack) |
---|
| 374 | +static int call_fib6_multipath_entry_notifier(struct notifier_block *nb, |
---|
| 375 | + enum fib_event_type event_type, |
---|
| 376 | + struct fib6_info *rt, |
---|
| 377 | + unsigned int nsiblings, |
---|
| 378 | + struct netlink_ext_ack *extack) |
---|
| 379 | +{ |
---|
| 380 | + struct fib6_entry_notifier_info info = { |
---|
| 381 | + .info.extack = extack, |
---|
| 382 | + .rt = rt, |
---|
| 383 | + .nsiblings = nsiblings, |
---|
| 384 | + }; |
---|
| 385 | + |
---|
| 386 | + return call_fib6_notifier(nb, event_type, &info.info); |
---|
| 387 | +} |
---|
| 388 | + |
---|
| 389 | +int call_fib6_entry_notifiers(struct net *net, |
---|
| 390 | + enum fib_event_type event_type, |
---|
| 391 | + struct fib6_info *rt, |
---|
| 392 | + struct netlink_ext_ack *extack) |
---|
408 | 393 | { |
---|
409 | 394 | struct fib6_entry_notifier_info info = { |
---|
410 | 395 | .info.extack = extack, |
---|
.. | .. |
---|
415 | 400 | return call_fib6_notifiers(net, event_type, &info.info); |
---|
416 | 401 | } |
---|
417 | 402 | |
---|
| 403 | +int call_fib6_multipath_entry_notifiers(struct net *net, |
---|
| 404 | + enum fib_event_type event_type, |
---|
| 405 | + struct fib6_info *rt, |
---|
| 406 | + unsigned int nsiblings, |
---|
| 407 | + struct netlink_ext_ack *extack) |
---|
| 408 | +{ |
---|
| 409 | + struct fib6_entry_notifier_info info = { |
---|
| 410 | + .info.extack = extack, |
---|
| 411 | + .rt = rt, |
---|
| 412 | + .nsiblings = nsiblings, |
---|
| 413 | + }; |
---|
| 414 | + |
---|
| 415 | + rt->fib6_table->fib_seq++; |
---|
| 416 | + return call_fib6_notifiers(net, event_type, &info.info); |
---|
| 417 | +} |
---|
| 418 | + |
---|
| 419 | +int call_fib6_entry_notifiers_replace(struct net *net, struct fib6_info *rt) |
---|
| 420 | +{ |
---|
| 421 | + struct fib6_entry_notifier_info info = { |
---|
| 422 | + .rt = rt, |
---|
| 423 | + .nsiblings = rt->fib6_nsiblings, |
---|
| 424 | + }; |
---|
| 425 | + |
---|
| 426 | + rt->fib6_table->fib_seq++; |
---|
| 427 | + return call_fib6_notifiers(net, FIB_EVENT_ENTRY_REPLACE, &info.info); |
---|
| 428 | +} |
---|
| 429 | + |
---|
418 | 430 | struct fib6_dump_arg { |
---|
419 | 431 | struct net *net; |
---|
420 | 432 | struct notifier_block *nb; |
---|
| 433 | + struct netlink_ext_ack *extack; |
---|
421 | 434 | }; |
---|
422 | 435 | |
---|
423 | | -static void fib6_rt_dump(struct fib6_info *rt, struct fib6_dump_arg *arg) |
---|
| 436 | +static int fib6_rt_dump(struct fib6_info *rt, struct fib6_dump_arg *arg) |
---|
424 | 437 | { |
---|
425 | | - if (rt == arg->net->ipv6.fib6_null_entry) |
---|
426 | | - return; |
---|
427 | | - call_fib6_entry_notifier(arg->nb, arg->net, FIB_EVENT_ENTRY_ADD, rt); |
---|
| 438 | + enum fib_event_type fib_event = FIB_EVENT_ENTRY_REPLACE; |
---|
| 439 | + int err; |
---|
| 440 | + |
---|
| 441 | + if (!rt || rt == arg->net->ipv6.fib6_null_entry) |
---|
| 442 | + return 0; |
---|
| 443 | + |
---|
| 444 | + if (rt->fib6_nsiblings) |
---|
| 445 | + err = call_fib6_multipath_entry_notifier(arg->nb, fib_event, |
---|
| 446 | + rt, |
---|
| 447 | + rt->fib6_nsiblings, |
---|
| 448 | + arg->extack); |
---|
| 449 | + else |
---|
| 450 | + err = call_fib6_entry_notifier(arg->nb, fib_event, rt, |
---|
| 451 | + arg->extack); |
---|
| 452 | + |
---|
| 453 | + return err; |
---|
428 | 454 | } |
---|
429 | 455 | |
---|
430 | 456 | static int fib6_node_dump(struct fib6_walker *w) |
---|
431 | 457 | { |
---|
432 | | - struct fib6_info *rt; |
---|
| 458 | + int err; |
---|
433 | 459 | |
---|
434 | | - for_each_fib6_walker_rt(w) |
---|
435 | | - fib6_rt_dump(rt, w->args); |
---|
| 460 | + err = fib6_rt_dump(w->leaf, w->args); |
---|
436 | 461 | w->leaf = NULL; |
---|
437 | | - return 0; |
---|
| 462 | + return err; |
---|
438 | 463 | } |
---|
439 | 464 | |
---|
440 | | -static void fib6_table_dump(struct net *net, struct fib6_table *tb, |
---|
441 | | - struct fib6_walker *w) |
---|
| 465 | +static int fib6_table_dump(struct net *net, struct fib6_table *tb, |
---|
| 466 | + struct fib6_walker *w) |
---|
442 | 467 | { |
---|
| 468 | + int err; |
---|
| 469 | + |
---|
443 | 470 | w->root = &tb->tb6_root; |
---|
444 | 471 | spin_lock_bh(&tb->tb6_lock); |
---|
445 | | - fib6_walk(net, w); |
---|
| 472 | + err = fib6_walk(net, w); |
---|
446 | 473 | spin_unlock_bh(&tb->tb6_lock); |
---|
| 474 | + return err; |
---|
447 | 475 | } |
---|
448 | 476 | |
---|
449 | 477 | /* Called with rcu_read_lock() */ |
---|
450 | | -int fib6_tables_dump(struct net *net, struct notifier_block *nb) |
---|
| 478 | +int fib6_tables_dump(struct net *net, struct notifier_block *nb, |
---|
| 479 | + struct netlink_ext_ack *extack) |
---|
451 | 480 | { |
---|
452 | 481 | struct fib6_dump_arg arg; |
---|
453 | 482 | struct fib6_walker *w; |
---|
454 | 483 | unsigned int h; |
---|
| 484 | + int err = 0; |
---|
455 | 485 | |
---|
456 | 486 | w = kzalloc(sizeof(*w), GFP_ATOMIC); |
---|
457 | 487 | if (!w) |
---|
.. | .. |
---|
460 | 490 | w->func = fib6_node_dump; |
---|
461 | 491 | arg.net = net; |
---|
462 | 492 | arg.nb = nb; |
---|
| 493 | + arg.extack = extack; |
---|
463 | 494 | w->args = &arg; |
---|
464 | 495 | |
---|
465 | 496 | for (h = 0; h < FIB6_TABLE_HASHSZ; h++) { |
---|
466 | 497 | struct hlist_head *head = &net->ipv6.fib_table_hash[h]; |
---|
467 | 498 | struct fib6_table *tb; |
---|
468 | 499 | |
---|
469 | | - hlist_for_each_entry_rcu(tb, head, tb6_hlist) |
---|
470 | | - fib6_table_dump(net, tb, w); |
---|
| 500 | + hlist_for_each_entry_rcu(tb, head, tb6_hlist) { |
---|
| 501 | + err = fib6_table_dump(net, tb, w); |
---|
| 502 | + if (err < 0) |
---|
| 503 | + goto out; |
---|
| 504 | + } |
---|
471 | 505 | } |
---|
472 | 506 | |
---|
| 507 | +out: |
---|
473 | 508 | kfree(w); |
---|
474 | 509 | |
---|
475 | | - return 0; |
---|
| 510 | + return err; |
---|
476 | 511 | } |
---|
477 | 512 | |
---|
478 | 513 | static int fib6_dump_node(struct fib6_walker *w) |
---|
.. | .. |
---|
481 | 516 | struct fib6_info *rt; |
---|
482 | 517 | |
---|
483 | 518 | for_each_fib6_walker_rt(w) { |
---|
484 | | - res = rt6_dump_route(rt, w->args); |
---|
485 | | - if (res < 0) { |
---|
| 519 | + res = rt6_dump_route(rt, w->args, w->skip_in_node); |
---|
| 520 | + if (res >= 0) { |
---|
486 | 521 | /* Frame is full, suspend walking */ |
---|
487 | 522 | w->leaf = rt; |
---|
| 523 | + |
---|
| 524 | + /* We'll restart from this node, so if some routes were |
---|
| 525 | + * already dumped, skip them next time. |
---|
| 526 | + */ |
---|
| 527 | + w->skip_in_node += res; |
---|
| 528 | + |
---|
488 | 529 | return 1; |
---|
489 | 530 | } |
---|
| 531 | + w->skip_in_node = 0; |
---|
490 | 532 | |
---|
491 | 533 | /* Multipath routes are dumped in one route with the |
---|
492 | 534 | * RTA_MULTIPATH attribute. Jump 'rt' to point to the |
---|
.. | .. |
---|
538 | 580 | if (cb->args[4] == 0) { |
---|
539 | 581 | w->count = 0; |
---|
540 | 582 | w->skip = 0; |
---|
| 583 | + w->skip_in_node = 0; |
---|
541 | 584 | |
---|
542 | 585 | spin_lock_bh(&table->tb6_lock); |
---|
543 | 586 | res = fib6_walk(net, w); |
---|
.. | .. |
---|
554 | 597 | w->state = FWS_INIT; |
---|
555 | 598 | w->node = w->root; |
---|
556 | 599 | w->skip = w->count; |
---|
| 600 | + w->skip_in_node = 0; |
---|
557 | 601 | } else |
---|
558 | 602 | w->skip = 0; |
---|
559 | 603 | |
---|
.. | .. |
---|
571 | 615 | |
---|
572 | 616 | static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) |
---|
573 | 617 | { |
---|
| 618 | + struct rt6_rtnl_dump_arg arg = { .filter.dump_exceptions = true, |
---|
| 619 | + .filter.dump_routes = true }; |
---|
| 620 | + const struct nlmsghdr *nlh = cb->nlh; |
---|
574 | 621 | struct net *net = sock_net(skb->sk); |
---|
575 | 622 | unsigned int h, s_h; |
---|
576 | 623 | unsigned int e = 0, s_e; |
---|
577 | | - struct rt6_rtnl_dump_arg arg; |
---|
578 | 624 | struct fib6_walker *w; |
---|
579 | 625 | struct fib6_table *tb; |
---|
580 | 626 | struct hlist_head *head; |
---|
581 | 627 | int res = 0; |
---|
582 | 628 | |
---|
583 | | - s_h = cb->args[0]; |
---|
584 | | - s_e = cb->args[1]; |
---|
| 629 | + if (cb->strict_check) { |
---|
| 630 | + int err; |
---|
| 631 | + |
---|
| 632 | + err = ip_valid_fib_dump_req(net, nlh, &arg.filter, cb); |
---|
| 633 | + if (err < 0) |
---|
| 634 | + return err; |
---|
| 635 | + } else if (nlmsg_len(nlh) >= sizeof(struct rtmsg)) { |
---|
| 636 | + struct rtmsg *rtm = nlmsg_data(nlh); |
---|
| 637 | + |
---|
| 638 | + if (rtm->rtm_flags & RTM_F_PREFIX) |
---|
| 639 | + arg.filter.flags = RTM_F_PREFIX; |
---|
| 640 | + } |
---|
585 | 641 | |
---|
586 | 642 | w = (void *)cb->args[2]; |
---|
587 | 643 | if (!w) { |
---|
.. | .. |
---|
607 | 663 | arg.net = net; |
---|
608 | 664 | w->args = &arg; |
---|
609 | 665 | |
---|
| 666 | + if (arg.filter.table_id) { |
---|
| 667 | + tb = fib6_get_table(net, arg.filter.table_id); |
---|
| 668 | + if (!tb) { |
---|
| 669 | + if (rtnl_msg_family(cb->nlh) != PF_INET6) |
---|
| 670 | + goto out; |
---|
| 671 | + |
---|
| 672 | + NL_SET_ERR_MSG_MOD(cb->extack, "FIB table does not exist"); |
---|
| 673 | + return -ENOENT; |
---|
| 674 | + } |
---|
| 675 | + |
---|
| 676 | + if (!cb->args[0]) { |
---|
| 677 | + res = fib6_dump_table(tb, skb, cb); |
---|
| 678 | + if (!res) |
---|
| 679 | + cb->args[0] = 1; |
---|
| 680 | + } |
---|
| 681 | + goto out; |
---|
| 682 | + } |
---|
| 683 | + |
---|
| 684 | + s_h = cb->args[0]; |
---|
| 685 | + s_e = cb->args[1]; |
---|
| 686 | + |
---|
610 | 687 | rcu_read_lock(); |
---|
611 | 688 | for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) { |
---|
612 | 689 | e = 0; |
---|
.. | .. |
---|
616 | 693 | goto next; |
---|
617 | 694 | res = fib6_dump_table(tb, skb, cb); |
---|
618 | 695 | if (res != 0) |
---|
619 | | - goto out; |
---|
| 696 | + goto out_unlock; |
---|
620 | 697 | next: |
---|
621 | 698 | e++; |
---|
622 | 699 | } |
---|
623 | 700 | } |
---|
624 | | -out: |
---|
| 701 | +out_unlock: |
---|
625 | 702 | rcu_read_unlock(); |
---|
626 | 703 | cb->args[1] = e; |
---|
627 | 704 | cb->args[0] = h; |
---|
628 | | - |
---|
| 705 | +out: |
---|
629 | 706 | res = res < 0 ? res : skb->len; |
---|
630 | 707 | if (res <= 0) |
---|
631 | 708 | fib6_dump_end(cb); |
---|
.. | .. |
---|
820 | 897 | |
---|
821 | 898 | RCU_INIT_POINTER(in->parent, pn); |
---|
822 | 899 | in->leaf = fn->leaf; |
---|
823 | | - atomic_inc(&rcu_dereference_protected(in->leaf, |
---|
824 | | - lockdep_is_held(&table->tb6_lock))->fib6_ref); |
---|
| 900 | + fib6_info_hold(rcu_dereference_protected(in->leaf, |
---|
| 901 | + lockdep_is_held(&table->tb6_lock))); |
---|
825 | 902 | |
---|
826 | 903 | /* update parent pointer */ |
---|
827 | 904 | if (dir) |
---|
.. | .. |
---|
873 | 950 | return ln; |
---|
874 | 951 | } |
---|
875 | 952 | |
---|
876 | | -static void fib6_drop_pcpu_from(struct fib6_info *f6i, |
---|
877 | | - const struct fib6_table *table) |
---|
| 953 | +static void __fib6_drop_pcpu_from(struct fib6_nh *fib6_nh, |
---|
| 954 | + const struct fib6_info *match, |
---|
| 955 | + const struct fib6_table *table) |
---|
878 | 956 | { |
---|
879 | 957 | int cpu; |
---|
880 | 958 | |
---|
881 | | - /* Make sure rt6_make_pcpu_route() wont add other percpu routes |
---|
882 | | - * while we are cleaning them here. |
---|
883 | | - */ |
---|
884 | | - f6i->fib6_destroying = 1; |
---|
885 | | - mb(); /* paired with the cmpxchg() in rt6_make_pcpu_route() */ |
---|
| 959 | + if (!fib6_nh->rt6i_pcpu) |
---|
| 960 | + return; |
---|
886 | 961 | |
---|
887 | 962 | /* release the reference to this fib entry from |
---|
888 | 963 | * all of its cached pcpu routes |
---|
.. | .. |
---|
891 | 966 | struct rt6_info **ppcpu_rt; |
---|
892 | 967 | struct rt6_info *pcpu_rt; |
---|
893 | 968 | |
---|
894 | | - ppcpu_rt = per_cpu_ptr(f6i->rt6i_pcpu, cpu); |
---|
| 969 | + ppcpu_rt = per_cpu_ptr(fib6_nh->rt6i_pcpu, cpu); |
---|
895 | 970 | pcpu_rt = *ppcpu_rt; |
---|
896 | | - if (pcpu_rt) { |
---|
| 971 | + |
---|
| 972 | + /* only dropping the 'from' reference if the cached route |
---|
| 973 | + * is using 'match'. The cached pcpu_rt->from only changes |
---|
| 974 | + * from a fib6_info to NULL (ip6_dst_destroy); it can never |
---|
| 975 | + * change from one fib6_info reference to another |
---|
| 976 | + */ |
---|
| 977 | + if (pcpu_rt && rcu_access_pointer(pcpu_rt->from) == match) { |
---|
897 | 978 | struct fib6_info *from; |
---|
898 | 979 | |
---|
899 | 980 | from = xchg((__force struct fib6_info **)&pcpu_rt->from, NULL); |
---|
900 | 981 | fib6_info_release(from); |
---|
901 | 982 | } |
---|
| 983 | + } |
---|
| 984 | +} |
---|
| 985 | + |
---|
| 986 | +struct fib6_nh_pcpu_arg { |
---|
| 987 | + struct fib6_info *from; |
---|
| 988 | + const struct fib6_table *table; |
---|
| 989 | +}; |
---|
| 990 | + |
---|
| 991 | +static int fib6_nh_drop_pcpu_from(struct fib6_nh *nh, void *_arg) |
---|
| 992 | +{ |
---|
| 993 | + struct fib6_nh_pcpu_arg *arg = _arg; |
---|
| 994 | + |
---|
| 995 | + __fib6_drop_pcpu_from(nh, arg->from, arg->table); |
---|
| 996 | + return 0; |
---|
| 997 | +} |
---|
| 998 | + |
---|
| 999 | +static void fib6_drop_pcpu_from(struct fib6_info *f6i, |
---|
| 1000 | + const struct fib6_table *table) |
---|
| 1001 | +{ |
---|
| 1002 | + /* Make sure rt6_make_pcpu_route() wont add other percpu routes |
---|
| 1003 | + * while we are cleaning them here. |
---|
| 1004 | + */ |
---|
| 1005 | + f6i->fib6_destroying = 1; |
---|
| 1006 | + mb(); /* paired with the cmpxchg() in rt6_make_pcpu_route() */ |
---|
| 1007 | + |
---|
| 1008 | + if (f6i->nh) { |
---|
| 1009 | + struct fib6_nh_pcpu_arg arg = { |
---|
| 1010 | + .from = f6i, |
---|
| 1011 | + .table = table |
---|
| 1012 | + }; |
---|
| 1013 | + |
---|
| 1014 | + nexthop_for_each_fib6_nh(f6i->nh, fib6_nh_drop_pcpu_from, |
---|
| 1015 | + &arg); |
---|
| 1016 | + } else { |
---|
| 1017 | + struct fib6_nh *fib6_nh; |
---|
| 1018 | + |
---|
| 1019 | + fib6_nh = f6i->fib6_nh; |
---|
| 1020 | + __fib6_drop_pcpu_from(fib6_nh, f6i, table); |
---|
902 | 1021 | } |
---|
903 | 1022 | } |
---|
904 | 1023 | |
---|
.. | .. |
---|
909 | 1028 | |
---|
910 | 1029 | /* Flush all cached dst in exception table */ |
---|
911 | 1030 | rt6_flush_exceptions(rt); |
---|
912 | | - if (rt->rt6i_pcpu) |
---|
913 | | - fib6_drop_pcpu_from(rt, table); |
---|
| 1031 | + fib6_drop_pcpu_from(rt, table); |
---|
914 | 1032 | |
---|
915 | | - if (atomic_read(&rt->fib6_ref) != 1) { |
---|
| 1033 | + if (rt->nh && !list_empty(&rt->nh_list)) |
---|
| 1034 | + list_del_init(&rt->nh_list); |
---|
| 1035 | + |
---|
| 1036 | + if (refcount_read(&rt->fib6_ref) != 1) { |
---|
916 | 1037 | /* This route is used as dummy address holder in some split |
---|
917 | 1038 | * nodes. It is not leaked, but it still holds other resources, |
---|
918 | 1039 | * which must be released in time. So, scan ascendant nodes |
---|
.. | .. |
---|
925 | 1046 | struct fib6_info *new_leaf; |
---|
926 | 1047 | if (!(fn->fn_flags & RTN_RTINFO) && leaf == rt) { |
---|
927 | 1048 | new_leaf = fib6_find_prefix(net, table, fn); |
---|
928 | | - atomic_inc(&new_leaf->fib6_ref); |
---|
| 1049 | + fib6_info_hold(new_leaf); |
---|
929 | 1050 | |
---|
930 | 1051 | rcu_assign_pointer(fn->leaf, new_leaf); |
---|
931 | 1052 | fib6_info_release(rt); |
---|
.. | .. |
---|
955 | 1076 | (info->nlh->nlmsg_flags & NLM_F_CREATE)); |
---|
956 | 1077 | int found = 0; |
---|
957 | 1078 | bool rt_can_ecmp = rt6_qualify_for_ecmp(rt); |
---|
| 1079 | + bool notify_sibling_rt = false; |
---|
958 | 1080 | u16 nlflags = NLM_F_EXCL; |
---|
959 | 1081 | int err; |
---|
960 | 1082 | |
---|
.. | .. |
---|
1047 | 1169 | |
---|
1048 | 1170 | /* Find the first route that have the same metric */ |
---|
1049 | 1171 | sibling = leaf; |
---|
| 1172 | + notify_sibling_rt = true; |
---|
1050 | 1173 | while (sibling) { |
---|
1051 | 1174 | if (sibling->fib6_metric == rt->fib6_metric && |
---|
1052 | 1175 | rt6_qualify_for_ecmp(sibling)) { |
---|
.. | .. |
---|
1056 | 1179 | } |
---|
1057 | 1180 | sibling = rcu_dereference_protected(sibling->fib6_next, |
---|
1058 | 1181 | lockdep_is_held(&rt->fib6_table->tb6_lock)); |
---|
| 1182 | + notify_sibling_rt = false; |
---|
1059 | 1183 | } |
---|
1060 | 1184 | /* For each sibling in the list, increment the counter of |
---|
1061 | 1185 | * siblings. BUG() if counters does not match, list of siblings |
---|
.. | .. |
---|
1082 | 1206 | add: |
---|
1083 | 1207 | nlflags |= NLM_F_CREATE; |
---|
1084 | 1208 | |
---|
1085 | | - err = call_fib6_entry_notifiers(info->nl_net, |
---|
1086 | | - FIB_EVENT_ENTRY_ADD, |
---|
1087 | | - rt, extack); |
---|
1088 | | - if (err) { |
---|
1089 | | - struct fib6_info *sibling, *next_sibling; |
---|
| 1209 | + /* The route should only be notified if it is the first |
---|
| 1210 | + * route in the node or if it is added as a sibling |
---|
| 1211 | + * route to the first route in the node. |
---|
| 1212 | + */ |
---|
| 1213 | + if (!info->skip_notify_kernel && |
---|
| 1214 | + (notify_sibling_rt || ins == &fn->leaf)) { |
---|
| 1215 | + enum fib_event_type fib_event; |
---|
1090 | 1216 | |
---|
1091 | | - /* If the route has siblings, then it first |
---|
1092 | | - * needs to be unlinked from them. |
---|
1093 | | - */ |
---|
1094 | | - if (!rt->fib6_nsiblings) |
---|
| 1217 | + if (notify_sibling_rt) |
---|
| 1218 | + fib_event = FIB_EVENT_ENTRY_APPEND; |
---|
| 1219 | + else |
---|
| 1220 | + fib_event = FIB_EVENT_ENTRY_REPLACE; |
---|
| 1221 | + err = call_fib6_entry_notifiers(info->nl_net, |
---|
| 1222 | + fib_event, rt, |
---|
| 1223 | + extack); |
---|
| 1224 | + if (err) { |
---|
| 1225 | + struct fib6_info *sibling, *next_sibling; |
---|
| 1226 | + |
---|
| 1227 | + /* If the route has siblings, then it first |
---|
| 1228 | + * needs to be unlinked from them. |
---|
| 1229 | + */ |
---|
| 1230 | + if (!rt->fib6_nsiblings) |
---|
| 1231 | + return err; |
---|
| 1232 | + |
---|
| 1233 | + list_for_each_entry_safe(sibling, next_sibling, |
---|
| 1234 | + &rt->fib6_siblings, |
---|
| 1235 | + fib6_siblings) |
---|
| 1236 | + sibling->fib6_nsiblings--; |
---|
| 1237 | + rt->fib6_nsiblings = 0; |
---|
| 1238 | + list_del_init(&rt->fib6_siblings); |
---|
| 1239 | + rt6_multipath_rebalance(next_sibling); |
---|
1095 | 1240 | return err; |
---|
1096 | | - |
---|
1097 | | - list_for_each_entry_safe(sibling, next_sibling, |
---|
1098 | | - &rt->fib6_siblings, |
---|
1099 | | - fib6_siblings) |
---|
1100 | | - sibling->fib6_nsiblings--; |
---|
1101 | | - rt->fib6_nsiblings = 0; |
---|
1102 | | - list_del_init(&rt->fib6_siblings); |
---|
1103 | | - rt6_multipath_rebalance(next_sibling); |
---|
1104 | | - return err; |
---|
| 1241 | + } |
---|
1105 | 1242 | } |
---|
1106 | 1243 | |
---|
1107 | 1244 | rcu_assign_pointer(rt->fib6_next, iter); |
---|
1108 | | - atomic_inc(&rt->fib6_ref); |
---|
| 1245 | + fib6_info_hold(rt); |
---|
1109 | 1246 | rcu_assign_pointer(rt->fib6_node, fn); |
---|
1110 | 1247 | rcu_assign_pointer(*ins, rt); |
---|
1111 | 1248 | if (!info->skip_notify) |
---|
.. | .. |
---|
1127 | 1264 | return -ENOENT; |
---|
1128 | 1265 | } |
---|
1129 | 1266 | |
---|
1130 | | - err = call_fib6_entry_notifiers(info->nl_net, |
---|
1131 | | - FIB_EVENT_ENTRY_REPLACE, |
---|
1132 | | - rt, extack); |
---|
1133 | | - if (err) |
---|
1134 | | - return err; |
---|
| 1267 | + if (!info->skip_notify_kernel && ins == &fn->leaf) { |
---|
| 1268 | + err = call_fib6_entry_notifiers(info->nl_net, |
---|
| 1269 | + FIB_EVENT_ENTRY_REPLACE, |
---|
| 1270 | + rt, extack); |
---|
| 1271 | + if (err) |
---|
| 1272 | + return err; |
---|
| 1273 | + } |
---|
1135 | 1274 | |
---|
1136 | | - atomic_inc(&rt->fib6_ref); |
---|
| 1275 | + fib6_info_hold(rt); |
---|
1137 | 1276 | rcu_assign_pointer(rt->fib6_node, fn); |
---|
1138 | 1277 | rt->fib6_next = iter->fib6_next; |
---|
1139 | 1278 | rcu_assign_pointer(*ins, rt); |
---|
.. | .. |
---|
1215 | 1354 | __fib6_update_sernum_upto_root(rt, fib6_new_sernum(net)); |
---|
1216 | 1355 | } |
---|
1217 | 1356 | |
---|
| 1357 | +/* allow ipv4 to update sernum via ipv6_stub */ |
---|
| 1358 | +void fib6_update_sernum_stub(struct net *net, struct fib6_info *f6i) |
---|
| 1359 | +{ |
---|
| 1360 | + spin_lock_bh(&f6i->fib6_table->tb6_lock); |
---|
| 1361 | + fib6_update_sernum_upto_root(net, f6i); |
---|
| 1362 | + spin_unlock_bh(&f6i->fib6_table->tb6_lock); |
---|
| 1363 | +} |
---|
| 1364 | + |
---|
1218 | 1365 | /* |
---|
1219 | 1366 | * Add routing information to the routing tree. |
---|
1220 | 1367 | * <destination addr>/<source addr> |
---|
.. | .. |
---|
1230 | 1377 | int err = -ENOMEM; |
---|
1231 | 1378 | int allow_create = 1; |
---|
1232 | 1379 | int replace_required = 0; |
---|
1233 | | - int sernum = fib6_new_sernum(info->nl_net); |
---|
1234 | 1380 | |
---|
1235 | 1381 | if (info->nlh) { |
---|
1236 | 1382 | if (!(info->nlh->nlmsg_flags & NLM_F_CREATE)) |
---|
.. | .. |
---|
1275 | 1421 | if (!sfn) |
---|
1276 | 1422 | goto failure; |
---|
1277 | 1423 | |
---|
1278 | | - atomic_inc(&info->nl_net->ipv6.fib6_null_entry->fib6_ref); |
---|
| 1424 | + fib6_info_hold(info->nl_net->ipv6.fib6_null_entry); |
---|
1279 | 1425 | rcu_assign_pointer(sfn->leaf, |
---|
1280 | 1426 | info->nl_net->ipv6.fib6_null_entry); |
---|
1281 | 1427 | sfn->fn_flags = RTN_ROOT; |
---|
.. | .. |
---|
1318 | 1464 | rcu_assign_pointer(fn->leaf, |
---|
1319 | 1465 | info->nl_net->ipv6.fib6_null_entry); |
---|
1320 | 1466 | } else { |
---|
1321 | | - atomic_inc(&rt->fib6_ref); |
---|
| 1467 | + fib6_info_hold(rt); |
---|
1322 | 1468 | rcu_assign_pointer(fn->leaf, rt); |
---|
1323 | 1469 | } |
---|
1324 | 1470 | } |
---|
.. | .. |
---|
1328 | 1474 | |
---|
1329 | 1475 | err = fib6_add_rt2node(fn, rt, info, extack); |
---|
1330 | 1476 | if (!err) { |
---|
1331 | | - __fib6_update_sernum_upto_root(rt, sernum); |
---|
| 1477 | + if (rt->nh) |
---|
| 1478 | + list_add(&rt->nh_list, &rt->nh->f6i_list); |
---|
| 1479 | + __fib6_update_sernum_upto_root(rt, fib6_new_sernum(info->nl_net)); |
---|
1332 | 1480 | fib6_start_gc(info->nl_net, rt); |
---|
1333 | 1481 | } |
---|
1334 | 1482 | |
---|
.. | .. |
---|
1364 | 1512 | } |
---|
1365 | 1513 | #endif |
---|
1366 | 1514 | goto failure; |
---|
| 1515 | + } else if (fib6_requires_src(rt)) { |
---|
| 1516 | + fib6_routes_require_src_inc(info->nl_net); |
---|
1367 | 1517 | } |
---|
1368 | 1518 | return err; |
---|
1369 | 1519 | |
---|
.. | .. |
---|
1664 | 1814 | |
---|
1665 | 1815 | children = 0; |
---|
1666 | 1816 | child = NULL; |
---|
1667 | | - if (fn_r) |
---|
1668 | | - child = fn_r, children |= 1; |
---|
1669 | | - if (fn_l) |
---|
1670 | | - child = fn_l, children |= 2; |
---|
| 1817 | + if (fn_r) { |
---|
| 1818 | + child = fn_r; |
---|
| 1819 | + children |= 1; |
---|
| 1820 | + } |
---|
| 1821 | + if (fn_l) { |
---|
| 1822 | + child = fn_l; |
---|
| 1823 | + children |= 2; |
---|
| 1824 | + } |
---|
1671 | 1825 | |
---|
1672 | 1826 | if (children == 3 || FIB6_SUBTREE(fn) |
---|
1673 | 1827 | #ifdef CONFIG_IPV6_SUBTREES |
---|
.. | .. |
---|
1746 | 1900 | static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn, |
---|
1747 | 1901 | struct fib6_info __rcu **rtp, struct nl_info *info) |
---|
1748 | 1902 | { |
---|
| 1903 | + struct fib6_info *leaf, *replace_rt = NULL; |
---|
1749 | 1904 | struct fib6_walker *w; |
---|
1750 | 1905 | struct fib6_info *rt = rcu_dereference_protected(*rtp, |
---|
1751 | 1906 | lockdep_is_held(&table->tb6_lock)); |
---|
1752 | 1907 | struct net *net = info->nl_net; |
---|
| 1908 | + bool notify_del = false; |
---|
1753 | 1909 | |
---|
1754 | 1910 | RT6_TRACE("fib6_del_route\n"); |
---|
| 1911 | + |
---|
| 1912 | + /* If the deleted route is the first in the node and it is not part of |
---|
| 1913 | + * a multipath route, then we need to replace it with the next route |
---|
| 1914 | + * in the node, if exists. |
---|
| 1915 | + */ |
---|
| 1916 | + leaf = rcu_dereference_protected(fn->leaf, |
---|
| 1917 | + lockdep_is_held(&table->tb6_lock)); |
---|
| 1918 | + if (leaf == rt && !rt->fib6_nsiblings) { |
---|
| 1919 | + if (rcu_access_pointer(rt->fib6_next)) |
---|
| 1920 | + replace_rt = rcu_dereference_protected(rt->fib6_next, |
---|
| 1921 | + lockdep_is_held(&table->tb6_lock)); |
---|
| 1922 | + else |
---|
| 1923 | + notify_del = true; |
---|
| 1924 | + } |
---|
1755 | 1925 | |
---|
1756 | 1926 | /* Unlink it */ |
---|
1757 | 1927 | *rtp = rt->fib6_next; |
---|
.. | .. |
---|
1767 | 1937 | if (rt->fib6_nsiblings) { |
---|
1768 | 1938 | struct fib6_info *sibling, *next_sibling; |
---|
1769 | 1939 | |
---|
| 1940 | + /* The route is deleted from a multipath route. If this |
---|
| 1941 | + * multipath route is the first route in the node, then we need |
---|
| 1942 | + * to emit a delete notification. Otherwise, we need to skip |
---|
| 1943 | + * the notification. |
---|
| 1944 | + */ |
---|
| 1945 | + if (rt->fib6_metric == leaf->fib6_metric && |
---|
| 1946 | + rt6_qualify_for_ecmp(leaf)) |
---|
| 1947 | + notify_del = true; |
---|
1770 | 1948 | list_for_each_entry_safe(sibling, next_sibling, |
---|
1771 | 1949 | &rt->fib6_siblings, fib6_siblings) |
---|
1772 | 1950 | sibling->fib6_nsiblings--; |
---|
.. | .. |
---|
1802 | 1980 | |
---|
1803 | 1981 | fib6_purge_rt(rt, fn, net); |
---|
1804 | 1982 | |
---|
1805 | | - call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, rt, NULL); |
---|
| 1983 | + if (!info->skip_notify_kernel) { |
---|
| 1984 | + if (notify_del) |
---|
| 1985 | + call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, |
---|
| 1986 | + rt, NULL); |
---|
| 1987 | + else if (replace_rt) |
---|
| 1988 | + call_fib6_entry_notifiers_replace(net, replace_rt); |
---|
| 1989 | + } |
---|
1806 | 1990 | if (!info->skip_notify) |
---|
1807 | 1991 | inet6_rt_notify(RTM_DELROUTE, rt, info, 0); |
---|
| 1992 | + |
---|
1808 | 1993 | fib6_info_release(rt); |
---|
1809 | 1994 | } |
---|
1810 | 1995 | |
---|
.. | .. |
---|
1836 | 2021 | struct fib6_info *cur = rcu_dereference_protected(*rtp, |
---|
1837 | 2022 | lockdep_is_held(&table->tb6_lock)); |
---|
1838 | 2023 | if (rt == cur) { |
---|
| 2024 | + if (fib6_requires_src(cur)) |
---|
| 2025 | + fib6_routes_require_src_dec(info->nl_net); |
---|
1839 | 2026 | fib6_del_route(table, fn, rtp, info); |
---|
1840 | 2027 | return 0; |
---|
1841 | 2028 | } |
---|
.. | .. |
---|
1890 | 2077 | continue; |
---|
1891 | 2078 | } |
---|
1892 | 2079 | w->state = FWS_L; |
---|
| 2080 | + fallthrough; |
---|
1893 | 2081 | #endif |
---|
1894 | | - /* fall through */ |
---|
1895 | 2082 | case FWS_L: |
---|
1896 | 2083 | left = rcu_dereference_protected(fn->left, 1); |
---|
1897 | 2084 | if (left) { |
---|
.. | .. |
---|
1900 | 2087 | continue; |
---|
1901 | 2088 | } |
---|
1902 | 2089 | w->state = FWS_R; |
---|
1903 | | - /* fall through */ |
---|
| 2090 | + fallthrough; |
---|
1904 | 2091 | case FWS_R: |
---|
1905 | 2092 | right = rcu_dereference_protected(fn->right, 1); |
---|
1906 | 2093 | if (right) { |
---|
.. | .. |
---|
1910 | 2097 | } |
---|
1911 | 2098 | w->state = FWS_C; |
---|
1912 | 2099 | w->leaf = rcu_dereference_protected(fn->leaf, 1); |
---|
1913 | | - /* fall through */ |
---|
| 2100 | + fallthrough; |
---|
1914 | 2101 | case FWS_C: |
---|
1915 | 2102 | if (w->leaf && fn->fn_flags & RTN_RTINFO) { |
---|
1916 | 2103 | int err; |
---|
.. | .. |
---|
1929 | 2116 | } |
---|
1930 | 2117 | skip: |
---|
1931 | 2118 | w->state = FWS_U; |
---|
1932 | | - /* fall through */ |
---|
| 2119 | + fallthrough; |
---|
1933 | 2120 | case FWS_U: |
---|
1934 | 2121 | if (fn == w->root) |
---|
1935 | 2122 | return 0; |
---|
.. | .. |
---|
1981 | 2168 | struct fib6_cleaner *c = container_of(w, struct fib6_cleaner, w); |
---|
1982 | 2169 | struct nl_info info = { |
---|
1983 | 2170 | .nl_net = c->net, |
---|
| 2171 | + .skip_notify = c->skip_notify, |
---|
1984 | 2172 | }; |
---|
1985 | 2173 | |
---|
1986 | 2174 | if (c->sernum != FIB6_NO_SERNUM_CHANGE && |
---|
.. | .. |
---|
2032 | 2220 | |
---|
2033 | 2221 | static void fib6_clean_tree(struct net *net, struct fib6_node *root, |
---|
2034 | 2222 | int (*func)(struct fib6_info *, void *arg), |
---|
2035 | | - int sernum, void *arg) |
---|
| 2223 | + int sernum, void *arg, bool skip_notify) |
---|
2036 | 2224 | { |
---|
2037 | 2225 | struct fib6_cleaner c; |
---|
2038 | 2226 | |
---|
.. | .. |
---|
2040 | 2228 | c.w.func = fib6_clean_node; |
---|
2041 | 2229 | c.w.count = 0; |
---|
2042 | 2230 | c.w.skip = 0; |
---|
| 2231 | + c.w.skip_in_node = 0; |
---|
2043 | 2232 | c.func = func; |
---|
2044 | 2233 | c.sernum = sernum; |
---|
2045 | 2234 | c.arg = arg; |
---|
2046 | 2235 | c.net = net; |
---|
| 2236 | + c.skip_notify = skip_notify; |
---|
2047 | 2237 | |
---|
2048 | 2238 | fib6_walk(net, &c.w); |
---|
2049 | 2239 | } |
---|
2050 | 2240 | |
---|
2051 | 2241 | static void __fib6_clean_all(struct net *net, |
---|
2052 | 2242 | int (*func)(struct fib6_info *, void *), |
---|
2053 | | - int sernum, void *arg) |
---|
| 2243 | + int sernum, void *arg, bool skip_notify) |
---|
2054 | 2244 | { |
---|
2055 | 2245 | struct fib6_table *table; |
---|
2056 | 2246 | struct hlist_head *head; |
---|
.. | .. |
---|
2062 | 2252 | hlist_for_each_entry_rcu(table, head, tb6_hlist) { |
---|
2063 | 2253 | spin_lock_bh(&table->tb6_lock); |
---|
2064 | 2254 | fib6_clean_tree(net, &table->tb6_root, |
---|
2065 | | - func, sernum, arg); |
---|
| 2255 | + func, sernum, arg, skip_notify); |
---|
2066 | 2256 | spin_unlock_bh(&table->tb6_lock); |
---|
2067 | 2257 | } |
---|
2068 | 2258 | } |
---|
.. | .. |
---|
2072 | 2262 | void fib6_clean_all(struct net *net, int (*func)(struct fib6_info *, void *), |
---|
2073 | 2263 | void *arg) |
---|
2074 | 2264 | { |
---|
2075 | | - __fib6_clean_all(net, func, FIB6_NO_SERNUM_CHANGE, arg); |
---|
| 2265 | + __fib6_clean_all(net, func, FIB6_NO_SERNUM_CHANGE, arg, false); |
---|
| 2266 | +} |
---|
| 2267 | + |
---|
| 2268 | +void fib6_clean_all_skip_notify(struct net *net, |
---|
| 2269 | + int (*func)(struct fib6_info *, void *), |
---|
| 2270 | + void *arg) |
---|
| 2271 | +{ |
---|
| 2272 | + __fib6_clean_all(net, func, FIB6_NO_SERNUM_CHANGE, arg, true); |
---|
2076 | 2273 | } |
---|
2077 | 2274 | |
---|
2078 | 2275 | static void fib6_flush_trees(struct net *net) |
---|
2079 | 2276 | { |
---|
2080 | 2277 | int new_sernum = fib6_new_sernum(net); |
---|
2081 | 2278 | |
---|
2082 | | - __fib6_clean_all(net, NULL, new_sernum, NULL); |
---|
| 2279 | + __fib6_clean_all(net, NULL, new_sernum, NULL, false); |
---|
2083 | 2280 | } |
---|
2084 | 2281 | |
---|
2085 | 2282 | /* |
---|
.. | .. |
---|
2279 | 2476 | } |
---|
2280 | 2477 | |
---|
2281 | 2478 | #ifdef CONFIG_PROC_FS |
---|
2282 | | -static int ipv6_route_seq_show(struct seq_file *seq, void *v) |
---|
| 2479 | +static int ipv6_route_native_seq_show(struct seq_file *seq, void *v) |
---|
2283 | 2480 | { |
---|
2284 | 2481 | struct fib6_info *rt = v; |
---|
2285 | 2482 | struct ipv6_route_iter *iter = seq->private; |
---|
| 2483 | + struct fib6_nh *fib6_nh = rt->fib6_nh; |
---|
| 2484 | + unsigned int flags = rt->fib6_flags; |
---|
2286 | 2485 | const struct net_device *dev; |
---|
| 2486 | + |
---|
| 2487 | + if (rt->nh) |
---|
| 2488 | + fib6_nh = nexthop_fib6_nh_bh(rt->nh); |
---|
2287 | 2489 | |
---|
2288 | 2490 | seq_printf(seq, "%pi6 %02x ", &rt->fib6_dst.addr, rt->fib6_dst.plen); |
---|
2289 | 2491 | |
---|
.. | .. |
---|
2292 | 2494 | #else |
---|
2293 | 2495 | seq_puts(seq, "00000000000000000000000000000000 00 "); |
---|
2294 | 2496 | #endif |
---|
2295 | | - if (rt->fib6_flags & RTF_GATEWAY) |
---|
2296 | | - seq_printf(seq, "%pi6", &rt->fib6_nh.nh_gw); |
---|
2297 | | - else |
---|
| 2497 | + if (fib6_nh->fib_nh_gw_family) { |
---|
| 2498 | + flags |= RTF_GATEWAY; |
---|
| 2499 | + seq_printf(seq, "%pi6", &fib6_nh->fib_nh_gw6); |
---|
| 2500 | + } else { |
---|
2298 | 2501 | seq_puts(seq, "00000000000000000000000000000000"); |
---|
| 2502 | + } |
---|
2299 | 2503 | |
---|
2300 | | - dev = rt->fib6_nh.nh_dev; |
---|
| 2504 | + dev = fib6_nh->fib_nh_dev; |
---|
2301 | 2505 | seq_printf(seq, " %08x %08x %08x %08x %8s\n", |
---|
2302 | | - rt->fib6_metric, atomic_read(&rt->fib6_ref), 0, |
---|
2303 | | - rt->fib6_flags, dev ? dev->name : ""); |
---|
| 2506 | + rt->fib6_metric, refcount_read(&rt->fib6_ref), 0, |
---|
| 2507 | + flags, dev ? dev->name : ""); |
---|
2304 | 2508 | iter->w.leaf = NULL; |
---|
2305 | 2509 | return 0; |
---|
2306 | 2510 | } |
---|
.. | .. |
---|
2434 | 2638 | return w->node && !(w->state == FWS_U && w->node == w->root); |
---|
2435 | 2639 | } |
---|
2436 | 2640 | |
---|
2437 | | -static void ipv6_route_seq_stop(struct seq_file *seq, void *v) |
---|
| 2641 | +static void ipv6_route_native_seq_stop(struct seq_file *seq, void *v) |
---|
2438 | 2642 | __releases(RCU_BH) |
---|
2439 | 2643 | { |
---|
2440 | 2644 | struct net *net = seq_file_net(seq); |
---|
.. | .. |
---|
2446 | 2650 | rcu_read_unlock_bh(); |
---|
2447 | 2651 | } |
---|
2448 | 2652 | |
---|
| 2653 | +#if IS_BUILTIN(CONFIG_IPV6) && defined(CONFIG_BPF_SYSCALL) |
---|
| 2654 | +static int ipv6_route_prog_seq_show(struct bpf_prog *prog, |
---|
| 2655 | + struct bpf_iter_meta *meta, |
---|
| 2656 | + void *v) |
---|
| 2657 | +{ |
---|
| 2658 | + struct bpf_iter__ipv6_route ctx; |
---|
| 2659 | + |
---|
| 2660 | + ctx.meta = meta; |
---|
| 2661 | + ctx.rt = v; |
---|
| 2662 | + return bpf_iter_run_prog(prog, &ctx); |
---|
| 2663 | +} |
---|
| 2664 | + |
---|
| 2665 | +static int ipv6_route_seq_show(struct seq_file *seq, void *v) |
---|
| 2666 | +{ |
---|
| 2667 | + struct ipv6_route_iter *iter = seq->private; |
---|
| 2668 | + struct bpf_iter_meta meta; |
---|
| 2669 | + struct bpf_prog *prog; |
---|
| 2670 | + int ret; |
---|
| 2671 | + |
---|
| 2672 | + meta.seq = seq; |
---|
| 2673 | + prog = bpf_iter_get_info(&meta, false); |
---|
| 2674 | + if (!prog) |
---|
| 2675 | + return ipv6_route_native_seq_show(seq, v); |
---|
| 2676 | + |
---|
| 2677 | + ret = ipv6_route_prog_seq_show(prog, &meta, v); |
---|
| 2678 | + iter->w.leaf = NULL; |
---|
| 2679 | + |
---|
| 2680 | + return ret; |
---|
| 2681 | +} |
---|
| 2682 | + |
---|
| 2683 | +static void ipv6_route_seq_stop(struct seq_file *seq, void *v) |
---|
| 2684 | +{ |
---|
| 2685 | + struct bpf_iter_meta meta; |
---|
| 2686 | + struct bpf_prog *prog; |
---|
| 2687 | + |
---|
| 2688 | + if (!v) { |
---|
| 2689 | + meta.seq = seq; |
---|
| 2690 | + prog = bpf_iter_get_info(&meta, true); |
---|
| 2691 | + if (prog) |
---|
| 2692 | + (void)ipv6_route_prog_seq_show(prog, &meta, v); |
---|
| 2693 | + } |
---|
| 2694 | + |
---|
| 2695 | + ipv6_route_native_seq_stop(seq, v); |
---|
| 2696 | +} |
---|
| 2697 | +#else |
---|
| 2698 | +static int ipv6_route_seq_show(struct seq_file *seq, void *v) |
---|
| 2699 | +{ |
---|
| 2700 | + return ipv6_route_native_seq_show(seq, v); |
---|
| 2701 | +} |
---|
| 2702 | + |
---|
| 2703 | +static void ipv6_route_seq_stop(struct seq_file *seq, void *v) |
---|
| 2704 | +{ |
---|
| 2705 | + ipv6_route_native_seq_stop(seq, v); |
---|
| 2706 | +} |
---|
| 2707 | +#endif |
---|
| 2708 | + |
---|
2449 | 2709 | const struct seq_operations ipv6_route_seq_ops = { |
---|
2450 | 2710 | .start = ipv6_route_seq_start, |
---|
2451 | 2711 | .next = ipv6_route_seq_next, |
---|