| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * INET An implementation of the TCP/IP protocol suite for the LINUX |
|---|
| 3 | 4 | * operating system. INET is implemented using the BSD Socket |
|---|
| .. | .. |
|---|
| 6 | 7 | * IPv4 Forwarding Information Base: FIB frontend. |
|---|
| 7 | 8 | * |
|---|
| 8 | 9 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> |
|---|
| 9 | | - * |
|---|
| 10 | | - * This program is free software; you can redistribute it and/or |
|---|
| 11 | | - * modify it under the terms of the GNU General Public License |
|---|
| 12 | | - * as published by the Free Software Foundation; either version |
|---|
| 13 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 14 | 10 | */ |
|---|
| 15 | 11 | |
|---|
| 16 | 12 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 43 | 39 | #include <net/sock.h> |
|---|
| 44 | 40 | #include <net/arp.h> |
|---|
| 45 | 41 | #include <net/ip_fib.h> |
|---|
| 42 | +#include <net/nexthop.h> |
|---|
| 46 | 43 | #include <net/rtnetlink.h> |
|---|
| 47 | 44 | #include <net/xfrm.h> |
|---|
| 48 | 45 | #include <net/l3mdev.h> |
|---|
| .. | .. |
|---|
| 72 | 69 | fail: |
|---|
| 73 | 70 | fib_free_table(main_table); |
|---|
| 74 | 71 | return -ENOMEM; |
|---|
| 75 | | -} |
|---|
| 76 | | - |
|---|
| 77 | | -static bool fib4_has_custom_rules(struct net *net) |
|---|
| 78 | | -{ |
|---|
| 79 | | - return false; |
|---|
| 80 | 72 | } |
|---|
| 81 | 73 | #else |
|---|
| 82 | 74 | |
|---|
| .. | .. |
|---|
| 127 | 119 | h = id & (FIB_TABLE_HASHSZ - 1); |
|---|
| 128 | 120 | |
|---|
| 129 | 121 | head = &net->ipv4.fib_table_hash[h]; |
|---|
| 130 | | - hlist_for_each_entry_rcu(tb, head, tb_hlist) { |
|---|
| 122 | + hlist_for_each_entry_rcu(tb, head, tb_hlist, |
|---|
| 123 | + lockdep_rtnl_is_held()) { |
|---|
| 131 | 124 | if (tb->tb_id == id) |
|---|
| 132 | 125 | return tb; |
|---|
| 133 | 126 | } |
|---|
| 134 | 127 | return NULL; |
|---|
| 135 | | -} |
|---|
| 136 | | - |
|---|
| 137 | | -static bool fib4_has_custom_rules(struct net *net) |
|---|
| 138 | | -{ |
|---|
| 139 | | - return net->ipv4.fib_has_custom_rules; |
|---|
| 140 | 128 | } |
|---|
| 141 | 129 | #endif /* CONFIG_IP_MULTIPLE_TABLES */ |
|---|
| 142 | 130 | |
|---|
| .. | .. |
|---|
| 192 | 180 | return 0; |
|---|
| 193 | 181 | } |
|---|
| 194 | 182 | |
|---|
| 195 | | -static void fib_flush(struct net *net) |
|---|
| 183 | +void fib_flush(struct net *net) |
|---|
| 196 | 184 | { |
|---|
| 197 | 185 | int flushed = 0; |
|---|
| 198 | 186 | unsigned int h; |
|---|
| .. | .. |
|---|
| 234 | 222 | if (table) { |
|---|
| 235 | 223 | ret = RTN_UNICAST; |
|---|
| 236 | 224 | if (!fib_table_lookup(table, &fl4, &res, FIB_LOOKUP_NOREF)) { |
|---|
| 237 | | - if (!dev || dev == res.fi->fib_dev) |
|---|
| 225 | + struct fib_nh_common *nhc = fib_info_nhc(res.fi, 0); |
|---|
| 226 | + |
|---|
| 227 | + if (!dev || dev == nhc->nhc_dev) |
|---|
| 238 | 228 | ret = res.type; |
|---|
| 239 | 229 | } |
|---|
| 240 | 230 | } |
|---|
| .. | .. |
|---|
| 307 | 297 | .flowi4_mark = vmark ? skb->mark : 0, |
|---|
| 308 | 298 | }; |
|---|
| 309 | 299 | if (!fib_lookup(net, &fl4, &res, 0)) |
|---|
| 310 | | - return FIB_RES_PREFSRC(net, res); |
|---|
| 300 | + return fib_result_prefsrc(net, &res); |
|---|
| 311 | 301 | } else { |
|---|
| 312 | 302 | scope = RT_SCOPE_LINK; |
|---|
| 313 | 303 | } |
|---|
| 314 | 304 | |
|---|
| 315 | 305 | return inet_select_addr(dev, ip_hdr(skb)->saddr, scope); |
|---|
| 316 | 306 | } |
|---|
| 307 | + |
|---|
| 308 | +bool fib_info_nh_uses_dev(struct fib_info *fi, const struct net_device *dev) |
|---|
| 309 | +{ |
|---|
| 310 | + bool dev_match = false; |
|---|
| 311 | +#ifdef CONFIG_IP_ROUTE_MULTIPATH |
|---|
| 312 | + if (unlikely(fi->nh)) { |
|---|
| 313 | + dev_match = nexthop_uses_dev(fi->nh, dev); |
|---|
| 314 | + } else { |
|---|
| 315 | + int ret; |
|---|
| 316 | + |
|---|
| 317 | + for (ret = 0; ret < fib_info_num_path(fi); ret++) { |
|---|
| 318 | + const struct fib_nh_common *nhc = fib_info_nhc(fi, ret); |
|---|
| 319 | + |
|---|
| 320 | + if (nhc_l3mdev_matches_dev(nhc, dev)) { |
|---|
| 321 | + dev_match = true; |
|---|
| 322 | + break; |
|---|
| 323 | + } |
|---|
| 324 | + } |
|---|
| 325 | + } |
|---|
| 326 | +#else |
|---|
| 327 | + if (fib_info_nhc(fi, 0)->nhc_dev == dev) |
|---|
| 328 | + dev_match = true; |
|---|
| 329 | +#endif |
|---|
| 330 | + |
|---|
| 331 | + return dev_match; |
|---|
| 332 | +} |
|---|
| 333 | +EXPORT_SYMBOL_GPL(fib_info_nh_uses_dev); |
|---|
| 317 | 334 | |
|---|
| 318 | 335 | /* Given (packet source, input interface) and optional (dst, oif, tos): |
|---|
| 319 | 336 | * - (main) check, that source is valid i.e. not broadcast or our local |
|---|
| .. | .. |
|---|
| 345 | 362 | fl4.flowi4_tun_key.tun_id = 0; |
|---|
| 346 | 363 | fl4.flowi4_flags = 0; |
|---|
| 347 | 364 | fl4.flowi4_uid = sock_net_uid(net, NULL); |
|---|
| 365 | + fl4.flowi4_multipath_hash = 0; |
|---|
| 348 | 366 | |
|---|
| 349 | 367 | no_addr = idev->ifa_list == NULL; |
|---|
| 350 | 368 | |
|---|
| .. | .. |
|---|
| 363 | 381 | (res.type != RTN_LOCAL || !IN_DEV_ACCEPT_LOCAL(idev))) |
|---|
| 364 | 382 | goto e_inval; |
|---|
| 365 | 383 | fib_combine_itag(itag, &res); |
|---|
| 366 | | - dev_match = false; |
|---|
| 367 | 384 | |
|---|
| 368 | | -#ifdef CONFIG_IP_ROUTE_MULTIPATH |
|---|
| 369 | | - for (ret = 0; ret < res.fi->fib_nhs; ret++) { |
|---|
| 370 | | - struct fib_nh *nh = &res.fi->fib_nh[ret]; |
|---|
| 371 | | - |
|---|
| 372 | | - if (nh->nh_dev == dev) { |
|---|
| 373 | | - dev_match = true; |
|---|
| 374 | | - break; |
|---|
| 375 | | - } else if (l3mdev_master_ifindex_rcu(nh->nh_dev) == dev->ifindex) { |
|---|
| 376 | | - dev_match = true; |
|---|
| 377 | | - break; |
|---|
| 378 | | - } |
|---|
| 379 | | - } |
|---|
| 380 | | -#else |
|---|
| 381 | | - if (FIB_RES_DEV(res) == dev) |
|---|
| 382 | | - dev_match = true; |
|---|
| 383 | | -#endif |
|---|
| 385 | + dev_match = fib_info_nh_uses_dev(res.fi, dev); |
|---|
| 386 | + /* This is not common, loopback packets retain skb_dst so normally they |
|---|
| 387 | + * would not even hit this slow path. |
|---|
| 388 | + */ |
|---|
| 389 | + dev_match = dev_match || (res.type == RTN_LOCAL && |
|---|
| 390 | + dev == net->loopback_dev); |
|---|
| 384 | 391 | if (dev_match) { |
|---|
| 385 | | - ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST; |
|---|
| 392 | + ret = FIB_RES_NHC(res)->nhc_scope >= RT_SCOPE_LINK; |
|---|
| 386 | 393 | return ret; |
|---|
| 387 | 394 | } |
|---|
| 388 | 395 | if (no_addr) |
|---|
| .. | .. |
|---|
| 394 | 401 | ret = 0; |
|---|
| 395 | 402 | if (fib_lookup(net, &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE) == 0) { |
|---|
| 396 | 403 | if (res.type == RTN_UNICAST) |
|---|
| 397 | | - ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST; |
|---|
| 404 | + ret = FIB_RES_NHC(res)->nhc_scope >= RT_SCOPE_LINK; |
|---|
| 398 | 405 | } |
|---|
| 399 | 406 | return ret; |
|---|
| 400 | 407 | |
|---|
| .. | .. |
|---|
| 532 | 539 | cfg->fc_oif = dev->ifindex; |
|---|
| 533 | 540 | cfg->fc_table = l3mdev_fib_table(dev); |
|---|
| 534 | 541 | if (colon) { |
|---|
| 535 | | - struct in_ifaddr *ifa; |
|---|
| 536 | | - struct in_device *in_dev = __in_dev_get_rtnl(dev); |
|---|
| 542 | + const struct in_ifaddr *ifa; |
|---|
| 543 | + struct in_device *in_dev; |
|---|
| 544 | + |
|---|
| 545 | + in_dev = __in_dev_get_rtnl(dev); |
|---|
| 537 | 546 | if (!in_dev) |
|---|
| 538 | 547 | return -ENODEV; |
|---|
| 548 | + |
|---|
| 539 | 549 | *colon = ':'; |
|---|
| 540 | | - for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) |
|---|
| 550 | + |
|---|
| 551 | + rcu_read_lock(); |
|---|
| 552 | + in_dev_for_each_ifa_rcu(ifa, in_dev) { |
|---|
| 541 | 553 | if (strcmp(ifa->ifa_label, devname) == 0) |
|---|
| 542 | 554 | break; |
|---|
| 555 | + } |
|---|
| 556 | + rcu_read_unlock(); |
|---|
| 557 | + |
|---|
| 543 | 558 | if (!ifa) |
|---|
| 544 | 559 | return -ENODEV; |
|---|
| 545 | 560 | cfg->fc_prefsrc = ifa->ifa_local; |
|---|
| .. | .. |
|---|
| 550 | 565 | if (rt->rt_gateway.sa_family == AF_INET && addr) { |
|---|
| 551 | 566 | unsigned int addr_type; |
|---|
| 552 | 567 | |
|---|
| 553 | | - cfg->fc_gw = addr; |
|---|
| 568 | + cfg->fc_gw4 = addr; |
|---|
| 569 | + cfg->fc_gw_family = AF_INET; |
|---|
| 554 | 570 | addr_type = inet_addr_type_table(net, addr, cfg->fc_table); |
|---|
| 555 | 571 | if (rt->rt_flags & RTF_GATEWAY && |
|---|
| 556 | 572 | addr_type == RTN_UNICAST) |
|---|
| .. | .. |
|---|
| 560 | 576 | if (cmd == SIOCDELRT) |
|---|
| 561 | 577 | return 0; |
|---|
| 562 | 578 | |
|---|
| 563 | | - if (rt->rt_flags & RTF_GATEWAY && !cfg->fc_gw) |
|---|
| 579 | + if (rt->rt_flags & RTF_GATEWAY && !cfg->fc_gw_family) |
|---|
| 564 | 580 | return -EINVAL; |
|---|
| 565 | 581 | |
|---|
| 566 | 582 | if (cfg->fc_scope == RT_SCOPE_NOWHERE) |
|---|
| .. | .. |
|---|
| 636 | 652 | } |
|---|
| 637 | 653 | |
|---|
| 638 | 654 | const struct nla_policy rtm_ipv4_policy[RTA_MAX + 1] = { |
|---|
| 655 | + [RTA_UNSPEC] = { .strict_start_type = RTA_DPORT + 1 }, |
|---|
| 639 | 656 | [RTA_DST] = { .type = NLA_U32 }, |
|---|
| 640 | 657 | [RTA_SRC] = { .type = NLA_U32 }, |
|---|
| 641 | 658 | [RTA_IIF] = { .type = NLA_U32 }, |
|---|
| .. | .. |
|---|
| 654 | 671 | [RTA_IP_PROTO] = { .type = NLA_U8 }, |
|---|
| 655 | 672 | [RTA_SPORT] = { .type = NLA_U16 }, |
|---|
| 656 | 673 | [RTA_DPORT] = { .type = NLA_U16 }, |
|---|
| 674 | + [RTA_NH_ID] = { .type = NLA_U32 }, |
|---|
| 657 | 675 | }; |
|---|
| 676 | + |
|---|
| 677 | +int fib_gw_from_via(struct fib_config *cfg, struct nlattr *nla, |
|---|
| 678 | + struct netlink_ext_ack *extack) |
|---|
| 679 | +{ |
|---|
| 680 | + struct rtvia *via; |
|---|
| 681 | + int alen; |
|---|
| 682 | + |
|---|
| 683 | + if (nla_len(nla) < offsetof(struct rtvia, rtvia_addr)) { |
|---|
| 684 | + NL_SET_ERR_MSG(extack, "Invalid attribute length for RTA_VIA"); |
|---|
| 685 | + return -EINVAL; |
|---|
| 686 | + } |
|---|
| 687 | + |
|---|
| 688 | + via = nla_data(nla); |
|---|
| 689 | + alen = nla_len(nla) - offsetof(struct rtvia, rtvia_addr); |
|---|
| 690 | + |
|---|
| 691 | + switch (via->rtvia_family) { |
|---|
| 692 | + case AF_INET: |
|---|
| 693 | + if (alen != sizeof(__be32)) { |
|---|
| 694 | + NL_SET_ERR_MSG(extack, "Invalid IPv4 address in RTA_VIA"); |
|---|
| 695 | + return -EINVAL; |
|---|
| 696 | + } |
|---|
| 697 | + cfg->fc_gw_family = AF_INET; |
|---|
| 698 | + cfg->fc_gw4 = *((__be32 *)via->rtvia_addr); |
|---|
| 699 | + break; |
|---|
| 700 | + case AF_INET6: |
|---|
| 701 | +#if IS_ENABLED(CONFIG_IPV6) |
|---|
| 702 | + if (alen != sizeof(struct in6_addr)) { |
|---|
| 703 | + NL_SET_ERR_MSG(extack, "Invalid IPv6 address in RTA_VIA"); |
|---|
| 704 | + return -EINVAL; |
|---|
| 705 | + } |
|---|
| 706 | + cfg->fc_gw_family = AF_INET6; |
|---|
| 707 | + cfg->fc_gw6 = *((struct in6_addr *)via->rtvia_addr); |
|---|
| 708 | +#else |
|---|
| 709 | + NL_SET_ERR_MSG(extack, "IPv6 support not enabled in kernel"); |
|---|
| 710 | + return -EINVAL; |
|---|
| 711 | +#endif |
|---|
| 712 | + break; |
|---|
| 713 | + default: |
|---|
| 714 | + NL_SET_ERR_MSG(extack, "Unsupported address family in RTA_VIA"); |
|---|
| 715 | + return -EINVAL; |
|---|
| 716 | + } |
|---|
| 717 | + |
|---|
| 718 | + return 0; |
|---|
| 719 | +} |
|---|
| 658 | 720 | |
|---|
| 659 | 721 | static int rtm_to_fib_config(struct net *net, struct sk_buff *skb, |
|---|
| 660 | 722 | struct nlmsghdr *nlh, struct fib_config *cfg, |
|---|
| 661 | 723 | struct netlink_ext_ack *extack) |
|---|
| 662 | 724 | { |
|---|
| 725 | + bool has_gw = false, has_via = false; |
|---|
| 663 | 726 | struct nlattr *attr; |
|---|
| 664 | 727 | int err, remaining; |
|---|
| 665 | 728 | struct rtmsg *rtm; |
|---|
| 666 | 729 | |
|---|
| 667 | | - err = nlmsg_validate(nlh, sizeof(*rtm), RTA_MAX, rtm_ipv4_policy, |
|---|
| 668 | | - extack); |
|---|
| 730 | + err = nlmsg_validate_deprecated(nlh, sizeof(*rtm), RTA_MAX, |
|---|
| 731 | + rtm_ipv4_policy, extack); |
|---|
| 669 | 732 | if (err < 0) |
|---|
| 670 | 733 | goto errout; |
|---|
| 671 | 734 | |
|---|
| .. | .. |
|---|
| 700 | 763 | cfg->fc_oif = nla_get_u32(attr); |
|---|
| 701 | 764 | break; |
|---|
| 702 | 765 | case RTA_GATEWAY: |
|---|
| 703 | | - cfg->fc_gw = nla_get_be32(attr); |
|---|
| 766 | + has_gw = true; |
|---|
| 767 | + cfg->fc_gw4 = nla_get_be32(attr); |
|---|
| 768 | + if (cfg->fc_gw4) |
|---|
| 769 | + cfg->fc_gw_family = AF_INET; |
|---|
| 704 | 770 | break; |
|---|
| 705 | 771 | case RTA_VIA: |
|---|
| 706 | | - NL_SET_ERR_MSG(extack, "IPv4 does not support RTA_VIA attribute"); |
|---|
| 707 | | - err = -EINVAL; |
|---|
| 708 | | - goto errout; |
|---|
| 772 | + has_via = true; |
|---|
| 773 | + err = fib_gw_from_via(cfg, attr, extack); |
|---|
| 774 | + if (err) |
|---|
| 775 | + goto errout; |
|---|
| 776 | + break; |
|---|
| 709 | 777 | case RTA_PRIORITY: |
|---|
| 710 | 778 | cfg->fc_priority = nla_get_u32(attr); |
|---|
| 711 | 779 | break; |
|---|
| .. | .. |
|---|
| 741 | 809 | if (err < 0) |
|---|
| 742 | 810 | goto errout; |
|---|
| 743 | 811 | break; |
|---|
| 812 | + case RTA_NH_ID: |
|---|
| 813 | + cfg->fc_nh_id = nla_get_u32(attr); |
|---|
| 814 | + break; |
|---|
| 744 | 815 | } |
|---|
| 745 | 816 | } |
|---|
| 817 | + |
|---|
| 818 | + if (cfg->fc_nh_id) { |
|---|
| 819 | + if (cfg->fc_oif || cfg->fc_gw_family || |
|---|
| 820 | + cfg->fc_encap || cfg->fc_mp) { |
|---|
| 821 | + NL_SET_ERR_MSG(extack, |
|---|
| 822 | + "Nexthop specification and nexthop id are mutually exclusive"); |
|---|
| 823 | + return -EINVAL; |
|---|
| 824 | + } |
|---|
| 825 | + } |
|---|
| 826 | + |
|---|
| 827 | + if (has_gw && has_via) { |
|---|
| 828 | + NL_SET_ERR_MSG(extack, |
|---|
| 829 | + "Nexthop configuration can not contain both GATEWAY and VIA"); |
|---|
| 830 | + return -EINVAL; |
|---|
| 831 | + } |
|---|
| 832 | + |
|---|
| 833 | + if (!cfg->fc_table) |
|---|
| 834 | + cfg->fc_table = RT_TABLE_MAIN; |
|---|
| 746 | 835 | |
|---|
| 747 | 836 | return 0; |
|---|
| 748 | 837 | errout: |
|---|
| .. | .. |
|---|
| 760 | 849 | err = rtm_to_fib_config(net, skb, nlh, &cfg, extack); |
|---|
| 761 | 850 | if (err < 0) |
|---|
| 762 | 851 | goto errout; |
|---|
| 852 | + |
|---|
| 853 | + if (cfg.fc_nh_id && !nexthop_find_by_id(net, cfg.fc_nh_id)) { |
|---|
| 854 | + NL_SET_ERR_MSG(extack, "Nexthop id does not exist"); |
|---|
| 855 | + err = -EINVAL; |
|---|
| 856 | + goto errout; |
|---|
| 857 | + } |
|---|
| 763 | 858 | |
|---|
| 764 | 859 | tb = fib_get_table(net, cfg.fc_table); |
|---|
| 765 | 860 | if (!tb) { |
|---|
| .. | .. |
|---|
| 798 | 893 | return err; |
|---|
| 799 | 894 | } |
|---|
| 800 | 895 | |
|---|
| 896 | +int ip_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh, |
|---|
| 897 | + struct fib_dump_filter *filter, |
|---|
| 898 | + struct netlink_callback *cb) |
|---|
| 899 | +{ |
|---|
| 900 | + struct netlink_ext_ack *extack = cb->extack; |
|---|
| 901 | + struct nlattr *tb[RTA_MAX + 1]; |
|---|
| 902 | + struct rtmsg *rtm; |
|---|
| 903 | + int err, i; |
|---|
| 904 | + |
|---|
| 905 | + ASSERT_RTNL(); |
|---|
| 906 | + |
|---|
| 907 | + if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) { |
|---|
| 908 | + NL_SET_ERR_MSG(extack, "Invalid header for FIB dump request"); |
|---|
| 909 | + return -EINVAL; |
|---|
| 910 | + } |
|---|
| 911 | + |
|---|
| 912 | + rtm = nlmsg_data(nlh); |
|---|
| 913 | + if (rtm->rtm_dst_len || rtm->rtm_src_len || rtm->rtm_tos || |
|---|
| 914 | + rtm->rtm_scope) { |
|---|
| 915 | + NL_SET_ERR_MSG(extack, "Invalid values in header for FIB dump request"); |
|---|
| 916 | + return -EINVAL; |
|---|
| 917 | + } |
|---|
| 918 | + |
|---|
| 919 | + if (rtm->rtm_flags & ~(RTM_F_CLONED | RTM_F_PREFIX)) { |
|---|
| 920 | + NL_SET_ERR_MSG(extack, "Invalid flags for FIB dump request"); |
|---|
| 921 | + return -EINVAL; |
|---|
| 922 | + } |
|---|
| 923 | + if (rtm->rtm_flags & RTM_F_CLONED) |
|---|
| 924 | + filter->dump_routes = false; |
|---|
| 925 | + else |
|---|
| 926 | + filter->dump_exceptions = false; |
|---|
| 927 | + |
|---|
| 928 | + filter->flags = rtm->rtm_flags; |
|---|
| 929 | + filter->protocol = rtm->rtm_protocol; |
|---|
| 930 | + filter->rt_type = rtm->rtm_type; |
|---|
| 931 | + filter->table_id = rtm->rtm_table; |
|---|
| 932 | + |
|---|
| 933 | + err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX, |
|---|
| 934 | + rtm_ipv4_policy, extack); |
|---|
| 935 | + if (err < 0) |
|---|
| 936 | + return err; |
|---|
| 937 | + |
|---|
| 938 | + for (i = 0; i <= RTA_MAX; ++i) { |
|---|
| 939 | + int ifindex; |
|---|
| 940 | + |
|---|
| 941 | + if (!tb[i]) |
|---|
| 942 | + continue; |
|---|
| 943 | + |
|---|
| 944 | + switch (i) { |
|---|
| 945 | + case RTA_TABLE: |
|---|
| 946 | + filter->table_id = nla_get_u32(tb[i]); |
|---|
| 947 | + break; |
|---|
| 948 | + case RTA_OIF: |
|---|
| 949 | + ifindex = nla_get_u32(tb[i]); |
|---|
| 950 | + filter->dev = __dev_get_by_index(net, ifindex); |
|---|
| 951 | + if (!filter->dev) |
|---|
| 952 | + return -ENODEV; |
|---|
| 953 | + break; |
|---|
| 954 | + default: |
|---|
| 955 | + NL_SET_ERR_MSG(extack, "Unsupported attribute in dump request"); |
|---|
| 956 | + return -EINVAL; |
|---|
| 957 | + } |
|---|
| 958 | + } |
|---|
| 959 | + |
|---|
| 960 | + if (filter->flags || filter->protocol || filter->rt_type || |
|---|
| 961 | + filter->table_id || filter->dev) { |
|---|
| 962 | + filter->filter_set = 1; |
|---|
| 963 | + cb->answer_flags = NLM_F_DUMP_FILTERED; |
|---|
| 964 | + } |
|---|
| 965 | + |
|---|
| 966 | + return 0; |
|---|
| 967 | +} |
|---|
| 968 | +EXPORT_SYMBOL_GPL(ip_valid_fib_dump_req); |
|---|
| 969 | + |
|---|
| 801 | 970 | static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) |
|---|
| 802 | 971 | { |
|---|
| 972 | + struct fib_dump_filter filter = { .dump_routes = true, |
|---|
| 973 | + .dump_exceptions = true }; |
|---|
| 974 | + const struct nlmsghdr *nlh = cb->nlh; |
|---|
| 803 | 975 | struct net *net = sock_net(skb->sk); |
|---|
| 804 | 976 | unsigned int h, s_h; |
|---|
| 805 | 977 | unsigned int e = 0, s_e; |
|---|
| .. | .. |
|---|
| 807 | 979 | struct hlist_head *head; |
|---|
| 808 | 980 | int dumped = 0, err; |
|---|
| 809 | 981 | |
|---|
| 810 | | - if (nlmsg_len(cb->nlh) >= sizeof(struct rtmsg) && |
|---|
| 811 | | - ((struct rtmsg *) nlmsg_data(cb->nlh))->rtm_flags & RTM_F_CLONED) |
|---|
| 982 | + if (cb->strict_check) { |
|---|
| 983 | + err = ip_valid_fib_dump_req(net, nlh, &filter, cb); |
|---|
| 984 | + if (err < 0) |
|---|
| 985 | + return err; |
|---|
| 986 | + } else if (nlmsg_len(nlh) >= sizeof(struct rtmsg)) { |
|---|
| 987 | + struct rtmsg *rtm = nlmsg_data(nlh); |
|---|
| 988 | + |
|---|
| 989 | + filter.flags = rtm->rtm_flags & (RTM_F_PREFIX | RTM_F_CLONED); |
|---|
| 990 | + } |
|---|
| 991 | + |
|---|
| 992 | + /* ipv4 does not use prefix flag */ |
|---|
| 993 | + if (filter.flags & RTM_F_PREFIX) |
|---|
| 812 | 994 | return skb->len; |
|---|
| 995 | + |
|---|
| 996 | + if (filter.table_id) { |
|---|
| 997 | + tb = fib_get_table(net, filter.table_id); |
|---|
| 998 | + if (!tb) { |
|---|
| 999 | + if (rtnl_msg_family(cb->nlh) != PF_INET) |
|---|
| 1000 | + return skb->len; |
|---|
| 1001 | + |
|---|
| 1002 | + NL_SET_ERR_MSG(cb->extack, "ipv4: FIB table does not exist"); |
|---|
| 1003 | + return -ENOENT; |
|---|
| 1004 | + } |
|---|
| 1005 | + |
|---|
| 1006 | + rcu_read_lock(); |
|---|
| 1007 | + err = fib_table_dump(tb, skb, cb, &filter); |
|---|
| 1008 | + rcu_read_unlock(); |
|---|
| 1009 | + return skb->len ? : err; |
|---|
| 1010 | + } |
|---|
| 813 | 1011 | |
|---|
| 814 | 1012 | s_h = cb->args[0]; |
|---|
| 815 | 1013 | s_e = cb->args[1]; |
|---|
| .. | .. |
|---|
| 825 | 1023 | if (dumped) |
|---|
| 826 | 1024 | memset(&cb->args[2], 0, sizeof(cb->args) - |
|---|
| 827 | 1025 | 2 * sizeof(cb->args[0])); |
|---|
| 828 | | - err = fib_table_dump(tb, skb, cb); |
|---|
| 1026 | + err = fib_table_dump(tb, skb, cb, &filter); |
|---|
| 829 | 1027 | if (err < 0) { |
|---|
| 830 | 1028 | if (likely(skb->len)) |
|---|
| 831 | 1029 | goto out; |
|---|
| .. | .. |
|---|
| 917 | 1115 | return; |
|---|
| 918 | 1116 | |
|---|
| 919 | 1117 | /* Add broadcast address, if it is explicitly assigned. */ |
|---|
| 920 | | - if (ifa->ifa_broadcast && ifa->ifa_broadcast != htonl(0xFFFFFFFF)) |
|---|
| 1118 | + if (ifa->ifa_broadcast && ifa->ifa_broadcast != htonl(0xFFFFFFFF)) { |
|---|
| 921 | 1119 | fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, |
|---|
| 922 | 1120 | prim, 0); |
|---|
| 1121 | + arp_invalidate(dev, ifa->ifa_broadcast, false); |
|---|
| 1122 | + } |
|---|
| 923 | 1123 | |
|---|
| 924 | 1124 | if (!ipv4_is_zeronet(prefix) && !(ifa->ifa_flags & IFA_F_SECONDARY) && |
|---|
| 925 | 1125 | (prefix != addr || ifa->ifa_prefixlen < 32)) { |
|---|
| .. | .. |
|---|
| 935 | 1135 | prim, 0); |
|---|
| 936 | 1136 | fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix | ~mask, |
|---|
| 937 | 1137 | 32, prim, 0); |
|---|
| 1138 | + arp_invalidate(dev, prefix | ~mask, false); |
|---|
| 938 | 1139 | } |
|---|
| 939 | 1140 | } |
|---|
| 940 | 1141 | } |
|---|
| .. | .. |
|---|
| 1015 | 1216 | * |
|---|
| 1016 | 1217 | * Scan address list to be sure that addresses are really gone. |
|---|
| 1017 | 1218 | */ |
|---|
| 1018 | | - |
|---|
| 1019 | | - for (ifa1 = in_dev->ifa_list; ifa1; ifa1 = ifa1->ifa_next) { |
|---|
| 1219 | + rcu_read_lock(); |
|---|
| 1220 | + in_dev_for_each_ifa_rcu(ifa1, in_dev) { |
|---|
| 1020 | 1221 | if (ifa1 == ifa) { |
|---|
| 1021 | 1222 | /* promotion, keep the IP */ |
|---|
| 1022 | 1223 | gone = 0; |
|---|
| .. | .. |
|---|
| 1084 | 1285 | } |
|---|
| 1085 | 1286 | } |
|---|
| 1086 | 1287 | } |
|---|
| 1288 | + rcu_read_unlock(); |
|---|
| 1087 | 1289 | |
|---|
| 1088 | 1290 | no_promotions: |
|---|
| 1089 | 1291 | if (!(ok & BRD_OK)) |
|---|
| .. | .. |
|---|
| 1253 | 1455 | struct netdev_notifier_info_ext *info_ext = ptr; |
|---|
| 1254 | 1456 | struct in_device *in_dev; |
|---|
| 1255 | 1457 | struct net *net = dev_net(dev); |
|---|
| 1458 | + struct in_ifaddr *ifa; |
|---|
| 1256 | 1459 | unsigned int flags; |
|---|
| 1257 | 1460 | |
|---|
| 1258 | 1461 | if (event == NETDEV_UNREGISTER) { |
|---|
| .. | .. |
|---|
| 1267 | 1470 | |
|---|
| 1268 | 1471 | switch (event) { |
|---|
| 1269 | 1472 | case NETDEV_UP: |
|---|
| 1270 | | - for_ifa(in_dev) { |
|---|
| 1473 | + in_dev_for_each_ifa_rtnl(ifa, in_dev) { |
|---|
| 1271 | 1474 | fib_add_ifaddr(ifa); |
|---|
| 1272 | | - } endfor_ifa(in_dev); |
|---|
| 1475 | + } |
|---|
| 1273 | 1476 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
|---|
| 1274 | 1477 | fib_sync_up(dev, RTNH_F_DEAD); |
|---|
| 1275 | 1478 | #endif |
|---|
| .. | .. |
|---|
| 1381 | 1584 | int error; |
|---|
| 1382 | 1585 | |
|---|
| 1383 | 1586 | #ifdef CONFIG_IP_ROUTE_CLASSID |
|---|
| 1384 | | - net->ipv4.fib_num_tclassid_users = 0; |
|---|
| 1587 | + atomic_set(&net->ipv4.fib_num_tclassid_users, 0); |
|---|
| 1385 | 1588 | #endif |
|---|
| 1386 | 1589 | error = ip_fib_net_init(net); |
|---|
| 1387 | 1590 | if (error < 0) |
|---|