| .. | .. |
|---|
| 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, |
|---|