.. | .. |
---|
| 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) |
---|
557 | 573 | cfg->fc_scope = RT_SCOPE_UNIVERSE; |
---|
558 | 574 | } |
---|
559 | 575 | |
---|
| 576 | + if (!cfg->fc_table) |
---|
| 577 | + cfg->fc_table = RT_TABLE_MAIN; |
---|
| 578 | + |
---|
560 | 579 | if (cmd == SIOCDELRT) |
---|
561 | 580 | return 0; |
---|
562 | 581 | |
---|
563 | | - if (rt->rt_flags & RTF_GATEWAY && !cfg->fc_gw) |
---|
| 582 | + if (rt->rt_flags & RTF_GATEWAY && !cfg->fc_gw_family) |
---|
564 | 583 | return -EINVAL; |
---|
565 | 584 | |
---|
566 | 585 | if (cfg->fc_scope == RT_SCOPE_NOWHERE) |
---|
.. | .. |
---|
636 | 655 | } |
---|
637 | 656 | |
---|
638 | 657 | const struct nla_policy rtm_ipv4_policy[RTA_MAX + 1] = { |
---|
| 658 | + [RTA_UNSPEC] = { .strict_start_type = RTA_DPORT + 1 }, |
---|
639 | 659 | [RTA_DST] = { .type = NLA_U32 }, |
---|
640 | 660 | [RTA_SRC] = { .type = NLA_U32 }, |
---|
641 | 661 | [RTA_IIF] = { .type = NLA_U32 }, |
---|
.. | .. |
---|
654 | 674 | [RTA_IP_PROTO] = { .type = NLA_U8 }, |
---|
655 | 675 | [RTA_SPORT] = { .type = NLA_U16 }, |
---|
656 | 676 | [RTA_DPORT] = { .type = NLA_U16 }, |
---|
| 677 | + [RTA_NH_ID] = { .type = NLA_U32 }, |
---|
657 | 678 | }; |
---|
| 679 | + |
---|
| 680 | +int fib_gw_from_via(struct fib_config *cfg, struct nlattr *nla, |
---|
| 681 | + struct netlink_ext_ack *extack) |
---|
| 682 | +{ |
---|
| 683 | + struct rtvia *via; |
---|
| 684 | + int alen; |
---|
| 685 | + |
---|
| 686 | + if (nla_len(nla) < offsetof(struct rtvia, rtvia_addr)) { |
---|
| 687 | + NL_SET_ERR_MSG(extack, "Invalid attribute length for RTA_VIA"); |
---|
| 688 | + return -EINVAL; |
---|
| 689 | + } |
---|
| 690 | + |
---|
| 691 | + via = nla_data(nla); |
---|
| 692 | + alen = nla_len(nla) - offsetof(struct rtvia, rtvia_addr); |
---|
| 693 | + |
---|
| 694 | + switch (via->rtvia_family) { |
---|
| 695 | + case AF_INET: |
---|
| 696 | + if (alen != sizeof(__be32)) { |
---|
| 697 | + NL_SET_ERR_MSG(extack, "Invalid IPv4 address in RTA_VIA"); |
---|
| 698 | + return -EINVAL; |
---|
| 699 | + } |
---|
| 700 | + cfg->fc_gw_family = AF_INET; |
---|
| 701 | + cfg->fc_gw4 = *((__be32 *)via->rtvia_addr); |
---|
| 702 | + break; |
---|
| 703 | + case AF_INET6: |
---|
| 704 | +#if IS_ENABLED(CONFIG_IPV6) |
---|
| 705 | + if (alen != sizeof(struct in6_addr)) { |
---|
| 706 | + NL_SET_ERR_MSG(extack, "Invalid IPv6 address in RTA_VIA"); |
---|
| 707 | + return -EINVAL; |
---|
| 708 | + } |
---|
| 709 | + cfg->fc_gw_family = AF_INET6; |
---|
| 710 | + cfg->fc_gw6 = *((struct in6_addr *)via->rtvia_addr); |
---|
| 711 | +#else |
---|
| 712 | + NL_SET_ERR_MSG(extack, "IPv6 support not enabled in kernel"); |
---|
| 713 | + return -EINVAL; |
---|
| 714 | +#endif |
---|
| 715 | + break; |
---|
| 716 | + default: |
---|
| 717 | + NL_SET_ERR_MSG(extack, "Unsupported address family in RTA_VIA"); |
---|
| 718 | + return -EINVAL; |
---|
| 719 | + } |
---|
| 720 | + |
---|
| 721 | + return 0; |
---|
| 722 | +} |
---|
658 | 723 | |
---|
659 | 724 | static int rtm_to_fib_config(struct net *net, struct sk_buff *skb, |
---|
660 | 725 | struct nlmsghdr *nlh, struct fib_config *cfg, |
---|
661 | 726 | struct netlink_ext_ack *extack) |
---|
662 | 727 | { |
---|
| 728 | + bool has_gw = false, has_via = false; |
---|
663 | 729 | struct nlattr *attr; |
---|
664 | 730 | int err, remaining; |
---|
665 | 731 | struct rtmsg *rtm; |
---|
666 | 732 | |
---|
667 | | - err = nlmsg_validate(nlh, sizeof(*rtm), RTA_MAX, rtm_ipv4_policy, |
---|
668 | | - extack); |
---|
| 733 | + err = nlmsg_validate_deprecated(nlh, sizeof(*rtm), RTA_MAX, |
---|
| 734 | + rtm_ipv4_policy, extack); |
---|
669 | 735 | if (err < 0) |
---|
670 | 736 | goto errout; |
---|
671 | 737 | |
---|
.. | .. |
---|
700 | 766 | cfg->fc_oif = nla_get_u32(attr); |
---|
701 | 767 | break; |
---|
702 | 768 | case RTA_GATEWAY: |
---|
703 | | - cfg->fc_gw = nla_get_be32(attr); |
---|
| 769 | + has_gw = true; |
---|
| 770 | + cfg->fc_gw4 = nla_get_be32(attr); |
---|
| 771 | + if (cfg->fc_gw4) |
---|
| 772 | + cfg->fc_gw_family = AF_INET; |
---|
704 | 773 | break; |
---|
705 | 774 | case RTA_VIA: |
---|
706 | | - NL_SET_ERR_MSG(extack, "IPv4 does not support RTA_VIA attribute"); |
---|
707 | | - err = -EINVAL; |
---|
708 | | - goto errout; |
---|
| 775 | + has_via = true; |
---|
| 776 | + err = fib_gw_from_via(cfg, attr, extack); |
---|
| 777 | + if (err) |
---|
| 778 | + goto errout; |
---|
| 779 | + break; |
---|
709 | 780 | case RTA_PRIORITY: |
---|
710 | 781 | cfg->fc_priority = nla_get_u32(attr); |
---|
711 | 782 | break; |
---|
.. | .. |
---|
741 | 812 | if (err < 0) |
---|
742 | 813 | goto errout; |
---|
743 | 814 | break; |
---|
| 815 | + case RTA_NH_ID: |
---|
| 816 | + cfg->fc_nh_id = nla_get_u32(attr); |
---|
| 817 | + break; |
---|
744 | 818 | } |
---|
745 | 819 | } |
---|
| 820 | + |
---|
| 821 | + if (cfg->fc_nh_id) { |
---|
| 822 | + if (cfg->fc_oif || cfg->fc_gw_family || |
---|
| 823 | + cfg->fc_encap || cfg->fc_mp) { |
---|
| 824 | + NL_SET_ERR_MSG(extack, |
---|
| 825 | + "Nexthop specification and nexthop id are mutually exclusive"); |
---|
| 826 | + return -EINVAL; |
---|
| 827 | + } |
---|
| 828 | + } |
---|
| 829 | + |
---|
| 830 | + if (has_gw && has_via) { |
---|
| 831 | + NL_SET_ERR_MSG(extack, |
---|
| 832 | + "Nexthop configuration can not contain both GATEWAY and VIA"); |
---|
| 833 | + return -EINVAL; |
---|
| 834 | + } |
---|
| 835 | + |
---|
| 836 | + if (!cfg->fc_table) |
---|
| 837 | + cfg->fc_table = RT_TABLE_MAIN; |
---|
746 | 838 | |
---|
747 | 839 | return 0; |
---|
748 | 840 | errout: |
---|
.. | .. |
---|
760 | 852 | err = rtm_to_fib_config(net, skb, nlh, &cfg, extack); |
---|
761 | 853 | if (err < 0) |
---|
762 | 854 | goto errout; |
---|
| 855 | + |
---|
| 856 | + if (cfg.fc_nh_id && !nexthop_find_by_id(net, cfg.fc_nh_id)) { |
---|
| 857 | + NL_SET_ERR_MSG(extack, "Nexthop id does not exist"); |
---|
| 858 | + err = -EINVAL; |
---|
| 859 | + goto errout; |
---|
| 860 | + } |
---|
763 | 861 | |
---|
764 | 862 | tb = fib_get_table(net, cfg.fc_table); |
---|
765 | 863 | if (!tb) { |
---|
.. | .. |
---|
798 | 896 | return err; |
---|
799 | 897 | } |
---|
800 | 898 | |
---|
| 899 | +int ip_valid_fib_dump_req(struct net *net, const struct nlmsghdr *nlh, |
---|
| 900 | + struct fib_dump_filter *filter, |
---|
| 901 | + struct netlink_callback *cb) |
---|
| 902 | +{ |
---|
| 903 | + struct netlink_ext_ack *extack = cb->extack; |
---|
| 904 | + struct nlattr *tb[RTA_MAX + 1]; |
---|
| 905 | + struct rtmsg *rtm; |
---|
| 906 | + int err, i; |
---|
| 907 | + |
---|
| 908 | + ASSERT_RTNL(); |
---|
| 909 | + |
---|
| 910 | + if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*rtm))) { |
---|
| 911 | + NL_SET_ERR_MSG(extack, "Invalid header for FIB dump request"); |
---|
| 912 | + return -EINVAL; |
---|
| 913 | + } |
---|
| 914 | + |
---|
| 915 | + rtm = nlmsg_data(nlh); |
---|
| 916 | + if (rtm->rtm_dst_len || rtm->rtm_src_len || rtm->rtm_tos || |
---|
| 917 | + rtm->rtm_scope) { |
---|
| 918 | + NL_SET_ERR_MSG(extack, "Invalid values in header for FIB dump request"); |
---|
| 919 | + return -EINVAL; |
---|
| 920 | + } |
---|
| 921 | + |
---|
| 922 | + if (rtm->rtm_flags & ~(RTM_F_CLONED | RTM_F_PREFIX)) { |
---|
| 923 | + NL_SET_ERR_MSG(extack, "Invalid flags for FIB dump request"); |
---|
| 924 | + return -EINVAL; |
---|
| 925 | + } |
---|
| 926 | + if (rtm->rtm_flags & RTM_F_CLONED) |
---|
| 927 | + filter->dump_routes = false; |
---|
| 928 | + else |
---|
| 929 | + filter->dump_exceptions = false; |
---|
| 930 | + |
---|
| 931 | + filter->flags = rtm->rtm_flags; |
---|
| 932 | + filter->protocol = rtm->rtm_protocol; |
---|
| 933 | + filter->rt_type = rtm->rtm_type; |
---|
| 934 | + filter->table_id = rtm->rtm_table; |
---|
| 935 | + |
---|
| 936 | + err = nlmsg_parse_deprecated_strict(nlh, sizeof(*rtm), tb, RTA_MAX, |
---|
| 937 | + rtm_ipv4_policy, extack); |
---|
| 938 | + if (err < 0) |
---|
| 939 | + return err; |
---|
| 940 | + |
---|
| 941 | + for (i = 0; i <= RTA_MAX; ++i) { |
---|
| 942 | + int ifindex; |
---|
| 943 | + |
---|
| 944 | + if (!tb[i]) |
---|
| 945 | + continue; |
---|
| 946 | + |
---|
| 947 | + switch (i) { |
---|
| 948 | + case RTA_TABLE: |
---|
| 949 | + filter->table_id = nla_get_u32(tb[i]); |
---|
| 950 | + break; |
---|
| 951 | + case RTA_OIF: |
---|
| 952 | + ifindex = nla_get_u32(tb[i]); |
---|
| 953 | + filter->dev = __dev_get_by_index(net, ifindex); |
---|
| 954 | + if (!filter->dev) |
---|
| 955 | + return -ENODEV; |
---|
| 956 | + break; |
---|
| 957 | + default: |
---|
| 958 | + NL_SET_ERR_MSG(extack, "Unsupported attribute in dump request"); |
---|
| 959 | + return -EINVAL; |
---|
| 960 | + } |
---|
| 961 | + } |
---|
| 962 | + |
---|
| 963 | + if (filter->flags || filter->protocol || filter->rt_type || |
---|
| 964 | + filter->table_id || filter->dev) { |
---|
| 965 | + filter->filter_set = 1; |
---|
| 966 | + cb->answer_flags = NLM_F_DUMP_FILTERED; |
---|
| 967 | + } |
---|
| 968 | + |
---|
| 969 | + return 0; |
---|
| 970 | +} |
---|
| 971 | +EXPORT_SYMBOL_GPL(ip_valid_fib_dump_req); |
---|
| 972 | + |
---|
801 | 973 | static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) |
---|
802 | 974 | { |
---|
| 975 | + struct fib_dump_filter filter = { .dump_routes = true, |
---|
| 976 | + .dump_exceptions = true }; |
---|
| 977 | + const struct nlmsghdr *nlh = cb->nlh; |
---|
803 | 978 | struct net *net = sock_net(skb->sk); |
---|
804 | 979 | unsigned int h, s_h; |
---|
805 | 980 | unsigned int e = 0, s_e; |
---|
.. | .. |
---|
807 | 982 | struct hlist_head *head; |
---|
808 | 983 | int dumped = 0, err; |
---|
809 | 984 | |
---|
810 | | - if (nlmsg_len(cb->nlh) >= sizeof(struct rtmsg) && |
---|
811 | | - ((struct rtmsg *) nlmsg_data(cb->nlh))->rtm_flags & RTM_F_CLONED) |
---|
| 985 | + if (cb->strict_check) { |
---|
| 986 | + err = ip_valid_fib_dump_req(net, nlh, &filter, cb); |
---|
| 987 | + if (err < 0) |
---|
| 988 | + return err; |
---|
| 989 | + } else if (nlmsg_len(nlh) >= sizeof(struct rtmsg)) { |
---|
| 990 | + struct rtmsg *rtm = nlmsg_data(nlh); |
---|
| 991 | + |
---|
| 992 | + filter.flags = rtm->rtm_flags & (RTM_F_PREFIX | RTM_F_CLONED); |
---|
| 993 | + } |
---|
| 994 | + |
---|
| 995 | + /* ipv4 does not use prefix flag */ |
---|
| 996 | + if (filter.flags & RTM_F_PREFIX) |
---|
812 | 997 | return skb->len; |
---|
| 998 | + |
---|
| 999 | + if (filter.table_id) { |
---|
| 1000 | + tb = fib_get_table(net, filter.table_id); |
---|
| 1001 | + if (!tb) { |
---|
| 1002 | + if (rtnl_msg_family(cb->nlh) != PF_INET) |
---|
| 1003 | + return skb->len; |
---|
| 1004 | + |
---|
| 1005 | + NL_SET_ERR_MSG(cb->extack, "ipv4: FIB table does not exist"); |
---|
| 1006 | + return -ENOENT; |
---|
| 1007 | + } |
---|
| 1008 | + |
---|
| 1009 | + rcu_read_lock(); |
---|
| 1010 | + err = fib_table_dump(tb, skb, cb, &filter); |
---|
| 1011 | + rcu_read_unlock(); |
---|
| 1012 | + return skb->len ? : err; |
---|
| 1013 | + } |
---|
813 | 1014 | |
---|
814 | 1015 | s_h = cb->args[0]; |
---|
815 | 1016 | s_e = cb->args[1]; |
---|
.. | .. |
---|
825 | 1026 | if (dumped) |
---|
826 | 1027 | memset(&cb->args[2], 0, sizeof(cb->args) - |
---|
827 | 1028 | 2 * sizeof(cb->args[0])); |
---|
828 | | - err = fib_table_dump(tb, skb, cb); |
---|
| 1029 | + err = fib_table_dump(tb, skb, cb, &filter); |
---|
829 | 1030 | if (err < 0) { |
---|
830 | 1031 | if (likely(skb->len)) |
---|
831 | 1032 | goto out; |
---|
.. | .. |
---|
917 | 1118 | return; |
---|
918 | 1119 | |
---|
919 | 1120 | /* Add broadcast address, if it is explicitly assigned. */ |
---|
920 | | - if (ifa->ifa_broadcast && ifa->ifa_broadcast != htonl(0xFFFFFFFF)) |
---|
| 1121 | + if (ifa->ifa_broadcast && ifa->ifa_broadcast != htonl(0xFFFFFFFF)) { |
---|
921 | 1122 | fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, |
---|
922 | 1123 | prim, 0); |
---|
| 1124 | + arp_invalidate(dev, ifa->ifa_broadcast, false); |
---|
| 1125 | + } |
---|
923 | 1126 | |
---|
924 | 1127 | if (!ipv4_is_zeronet(prefix) && !(ifa->ifa_flags & IFA_F_SECONDARY) && |
---|
925 | 1128 | (prefix != addr || ifa->ifa_prefixlen < 32)) { |
---|
.. | .. |
---|
935 | 1138 | prim, 0); |
---|
936 | 1139 | fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix | ~mask, |
---|
937 | 1140 | 32, prim, 0); |
---|
| 1141 | + arp_invalidate(dev, prefix | ~mask, false); |
---|
938 | 1142 | } |
---|
939 | 1143 | } |
---|
940 | 1144 | } |
---|
.. | .. |
---|
1015 | 1219 | * |
---|
1016 | 1220 | * Scan address list to be sure that addresses are really gone. |
---|
1017 | 1221 | */ |
---|
1018 | | - |
---|
1019 | | - for (ifa1 = in_dev->ifa_list; ifa1; ifa1 = ifa1->ifa_next) { |
---|
| 1222 | + rcu_read_lock(); |
---|
| 1223 | + in_dev_for_each_ifa_rcu(ifa1, in_dev) { |
---|
1020 | 1224 | if (ifa1 == ifa) { |
---|
1021 | 1225 | /* promotion, keep the IP */ |
---|
1022 | 1226 | gone = 0; |
---|
.. | .. |
---|
1084 | 1288 | } |
---|
1085 | 1289 | } |
---|
1086 | 1290 | } |
---|
| 1291 | + rcu_read_unlock(); |
---|
1087 | 1292 | |
---|
1088 | 1293 | no_promotions: |
---|
1089 | 1294 | if (!(ok & BRD_OK)) |
---|
.. | .. |
---|
1253 | 1458 | struct netdev_notifier_info_ext *info_ext = ptr; |
---|
1254 | 1459 | struct in_device *in_dev; |
---|
1255 | 1460 | struct net *net = dev_net(dev); |
---|
| 1461 | + struct in_ifaddr *ifa; |
---|
1256 | 1462 | unsigned int flags; |
---|
1257 | 1463 | |
---|
1258 | 1464 | if (event == NETDEV_UNREGISTER) { |
---|
.. | .. |
---|
1267 | 1473 | |
---|
1268 | 1474 | switch (event) { |
---|
1269 | 1475 | case NETDEV_UP: |
---|
1270 | | - for_ifa(in_dev) { |
---|
| 1476 | + in_dev_for_each_ifa_rtnl(ifa, in_dev) { |
---|
1271 | 1477 | fib_add_ifaddr(ifa); |
---|
1272 | | - } endfor_ifa(in_dev); |
---|
| 1478 | + } |
---|
1273 | 1479 | #ifdef CONFIG_IP_ROUTE_MULTIPATH |
---|
1274 | 1480 | fib_sync_up(dev, RTNH_F_DEAD); |
---|
1275 | 1481 | #endif |
---|
.. | .. |
---|
1381 | 1587 | int error; |
---|
1382 | 1588 | |
---|
1383 | 1589 | #ifdef CONFIG_IP_ROUTE_CLASSID |
---|
1384 | | - net->ipv4.fib_num_tclassid_users = 0; |
---|
| 1590 | + atomic_set(&net->ipv4.fib_num_tclassid_users, 0); |
---|
1385 | 1591 | #endif |
---|
1386 | 1592 | error = ip_fib_net_init(net); |
---|
1387 | 1593 | if (error < 0) |
---|