diff options
| -rw-r--r-- | net/ipv6/addrconf.c | 184 |
1 files changed, 109 insertions, 75 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 85100c1ac761..6913a82f4669 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
| @@ -900,15 +900,95 @@ out: | |||
| 900 | goto out2; | 900 | goto out2; |
| 901 | } | 901 | } |
| 902 | 902 | ||
| 903 | enum cleanup_prefix_rt_t { | ||
| 904 | CLEANUP_PREFIX_RT_NOP, /* no cleanup action for prefix route */ | ||
| 905 | CLEANUP_PREFIX_RT_DEL, /* delete the prefix route */ | ||
| 906 | CLEANUP_PREFIX_RT_EXPIRE, /* update the lifetime of the prefix route */ | ||
| 907 | }; | ||
| 908 | |||
| 909 | /* | ||
| 910 | * Check, whether the prefix for ifp would still need a prefix route | ||
| 911 | * after deleting ifp. The function returns one of the CLEANUP_PREFIX_RT_* | ||
| 912 | * constants. | ||
| 913 | * | ||
| 914 | * 1) we don't purge prefix if address was not permanent. | ||
| 915 | * prefix is managed by its own lifetime. | ||
| 916 | * 2) we also don't purge, if the address was IFA_F_NOPREFIXROUTE. | ||
| 917 | * 3) if there are no addresses, delete prefix. | ||
| 918 | * 4) if there are still other permanent address(es), | ||
| 919 | * corresponding prefix is still permanent. | ||
| 920 | * 5) if there are still other addresses with IFA_F_NOPREFIXROUTE, | ||
| 921 | * don't purge the prefix, assume user space is managing it. | ||
| 922 | * 6) otherwise, update prefix lifetime to the | ||
| 923 | * longest valid lifetime among the corresponding | ||
| 924 | * addresses on the device. | ||
| 925 | * Note: subsequent RA will update lifetime. | ||
| 926 | **/ | ||
| 927 | static enum cleanup_prefix_rt_t | ||
| 928 | check_cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long *expires) | ||
| 929 | { | ||
| 930 | struct inet6_ifaddr *ifa; | ||
| 931 | struct inet6_dev *idev = ifp->idev; | ||
| 932 | unsigned long lifetime; | ||
| 933 | enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_DEL; | ||
| 934 | |||
| 935 | *expires = jiffies; | ||
| 936 | |||
| 937 | list_for_each_entry(ifa, &idev->addr_list, if_list) { | ||
| 938 | if (ifa == ifp) | ||
| 939 | continue; | ||
| 940 | if (!ipv6_prefix_equal(&ifa->addr, &ifp->addr, | ||
| 941 | ifp->prefix_len)) | ||
| 942 | continue; | ||
| 943 | if (ifa->flags & (IFA_F_PERMANENT | IFA_F_NOPREFIXROUTE)) | ||
| 944 | return CLEANUP_PREFIX_RT_NOP; | ||
| 945 | |||
| 946 | action = CLEANUP_PREFIX_RT_EXPIRE; | ||
| 947 | |||
| 948 | spin_lock(&ifa->lock); | ||
| 949 | |||
| 950 | lifetime = addrconf_timeout_fixup(ifa->valid_lft, HZ); | ||
| 951 | /* | ||
| 952 | * Note: Because this address is | ||
| 953 | * not permanent, lifetime < | ||
| 954 | * LONG_MAX / HZ here. | ||
| 955 | */ | ||
| 956 | if (time_before(*expires, ifa->tstamp + lifetime * HZ)) | ||
| 957 | *expires = ifa->tstamp + lifetime * HZ; | ||
| 958 | spin_unlock(&ifa->lock); | ||
| 959 | } | ||
| 960 | |||
| 961 | return action; | ||
| 962 | } | ||
| 963 | |||
| 964 | static void | ||
| 965 | cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long expires, bool del_rt) | ||
| 966 | { | ||
| 967 | struct rt6_info *rt; | ||
| 968 | |||
| 969 | rt = addrconf_get_prefix_route(&ifp->addr, | ||
| 970 | ifp->prefix_len, | ||
| 971 | ifp->idev->dev, | ||
| 972 | 0, RTF_GATEWAY | RTF_DEFAULT); | ||
| 973 | if (rt) { | ||
| 974 | if (del_rt) | ||
| 975 | ip6_del_rt(rt); | ||
| 976 | else { | ||
| 977 | if (!(rt->rt6i_flags & RTF_EXPIRES)) | ||
| 978 | rt6_set_expires(rt, expires); | ||
| 979 | ip6_rt_put(rt); | ||
| 980 | } | ||
| 981 | } | ||
| 982 | } | ||
| 983 | |||
| 984 | |||
| 903 | /* This function wants to get referenced ifp and releases it before return */ | 985 | /* This function wants to get referenced ifp and releases it before return */ |
| 904 | 986 | ||
| 905 | static void ipv6_del_addr(struct inet6_ifaddr *ifp) | 987 | static void ipv6_del_addr(struct inet6_ifaddr *ifp) |
| 906 | { | 988 | { |
| 907 | struct inet6_ifaddr *ifa, *ifn; | ||
| 908 | struct inet6_dev *idev = ifp->idev; | ||
| 909 | int state; | 989 | int state; |
| 910 | int deleted = 0, onlink = 0; | 990 | enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_NOP; |
| 911 | unsigned long expires = jiffies; | 991 | unsigned long expires; |
| 912 | 992 | ||
| 913 | spin_lock_bh(&ifp->state_lock); | 993 | spin_lock_bh(&ifp->state_lock); |
| 914 | state = ifp->state; | 994 | state = ifp->state; |
| @@ -922,7 +1002,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) | |||
| 922 | hlist_del_init_rcu(&ifp->addr_lst); | 1002 | hlist_del_init_rcu(&ifp->addr_lst); |
| 923 | spin_unlock_bh(&addrconf_hash_lock); | 1003 | spin_unlock_bh(&addrconf_hash_lock); |
| 924 | 1004 | ||
| 925 | write_lock_bh(&idev->lock); | 1005 | write_lock_bh(&ifp->idev->lock); |
| 926 | 1006 | ||
| 927 | if (ifp->flags&IFA_F_TEMPORARY) { | 1007 | if (ifp->flags&IFA_F_TEMPORARY) { |
| 928 | list_del(&ifp->tmp_list); | 1008 | list_del(&ifp->tmp_list); |
| @@ -933,45 +1013,13 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) | |||
| 933 | __in6_ifa_put(ifp); | 1013 | __in6_ifa_put(ifp); |
| 934 | } | 1014 | } |
| 935 | 1015 | ||
| 936 | list_for_each_entry_safe(ifa, ifn, &idev->addr_list, if_list) { | 1016 | if (ifp->flags & IFA_F_PERMANENT && !(ifp->flags & IFA_F_NOPREFIXROUTE)) |
| 937 | if (ifa == ifp) { | 1017 | action = check_cleanup_prefix_route(ifp, &expires); |
| 938 | list_del_init(&ifp->if_list); | ||
| 939 | __in6_ifa_put(ifp); | ||
| 940 | 1018 | ||
| 941 | if (!(ifp->flags & IFA_F_PERMANENT) || onlink > 0) | 1019 | list_del_init(&ifp->if_list); |
| 942 | break; | 1020 | __in6_ifa_put(ifp); |
| 943 | deleted = 1; | 1021 | |
| 944 | continue; | 1022 | write_unlock_bh(&ifp->idev->lock); |
| 945 | } else if (ifp->flags & IFA_F_PERMANENT) { | ||
| 946 | if (ipv6_prefix_equal(&ifa->addr, &ifp->addr, | ||
| 947 | ifp->prefix_len)) { | ||
| 948 | if (ifa->flags & IFA_F_PERMANENT) { | ||
| 949 | onlink = 1; | ||
| 950 | if (deleted) | ||
| 951 | break; | ||
| 952 | } else { | ||
| 953 | unsigned long lifetime; | ||
| 954 | |||
| 955 | if (!onlink) | ||
| 956 | onlink = -1; | ||
| 957 | |||
| 958 | spin_lock(&ifa->lock); | ||
| 959 | |||
| 960 | lifetime = addrconf_timeout_fixup(ifa->valid_lft, HZ); | ||
| 961 | /* | ||
| 962 | * Note: Because this address is | ||
| 963 | * not permanent, lifetime < | ||
| 964 | * LONG_MAX / HZ here. | ||
| 965 | */ | ||
| 966 | if (time_before(expires, | ||
| 967 | ifa->tstamp + lifetime * HZ)) | ||
| 968 | expires = ifa->tstamp + lifetime * HZ; | ||
| 969 | spin_unlock(&ifa->lock); | ||
| 970 | } | ||
| 971 | } | ||
| 972 | } | ||
| 973 | } | ||
| 974 | write_unlock_bh(&idev->lock); | ||
| 975 | 1023 | ||
| 976 | addrconf_del_dad_timer(ifp); | 1024 | addrconf_del_dad_timer(ifp); |
| 977 | 1025 | ||
| @@ -979,38 +1027,9 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) | |||
| 979 | 1027 | ||
| 980 | inet6addr_notifier_call_chain(NETDEV_DOWN, ifp); | 1028 | inet6addr_notifier_call_chain(NETDEV_DOWN, ifp); |
| 981 | 1029 | ||
| 982 | /* | 1030 | if (action != CLEANUP_PREFIX_RT_NOP) { |
| 983 | * Purge or update corresponding prefix | 1031 | cleanup_prefix_route(ifp, expires, |
| 984 | * | 1032 | action == CLEANUP_PREFIX_RT_DEL); |
| 985 | * 1) we don't purge prefix here if address was not permanent. | ||
| 986 | * prefix is managed by its own lifetime. | ||
| 987 | * 2) if there are no addresses, delete prefix. | ||
| 988 | * 3) if there are still other permanent address(es), | ||
| 989 | * corresponding prefix is still permanent. | ||
| 990 | * 4) otherwise, update prefix lifetime to the | ||
| 991 | * longest valid lifetime among the corresponding | ||
| 992 | * addresses on the device. | ||
| 993 | * Note: subsequent RA will update lifetime. | ||
| 994 | * | ||
| 995 | * --yoshfuji | ||
| 996 | */ | ||
| 997 | if ((ifp->flags & IFA_F_PERMANENT) && onlink < 1) { | ||
| 998 | struct rt6_info *rt; | ||
| 999 | |||
| 1000 | rt = addrconf_get_prefix_route(&ifp->addr, | ||
| 1001 | ifp->prefix_len, | ||
| 1002 | ifp->idev->dev, | ||
| 1003 | 0, RTF_GATEWAY | RTF_DEFAULT); | ||
| 1004 | |||
| 1005 | if (rt) { | ||
| 1006 | if (onlink == 0) { | ||
| 1007 | ip6_del_rt(rt); | ||
| 1008 | rt = NULL; | ||
| 1009 | } else if (!(rt->rt6i_flags & RTF_EXPIRES)) { | ||
| 1010 | rt6_set_expires(rt, expires); | ||
| 1011 | } | ||
| 1012 | } | ||
| 1013 | ip6_rt_put(rt); | ||
| 1014 | } | 1033 | } |
| 1015 | 1034 | ||
| 1016 | /* clean up prefsrc entries */ | 1035 | /* clean up prefsrc entries */ |
| @@ -3636,6 +3655,7 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags, | |||
| 3636 | clock_t expires; | 3655 | clock_t expires; |
| 3637 | unsigned long timeout; | 3656 | unsigned long timeout; |
| 3638 | bool was_managetempaddr; | 3657 | bool was_managetempaddr; |
| 3658 | bool had_prefixroute; | ||
| 3639 | 3659 | ||
| 3640 | if (!valid_lft || (prefered_lft > valid_lft)) | 3660 | if (!valid_lft || (prefered_lft > valid_lft)) |
| 3641 | return -EINVAL; | 3661 | return -EINVAL; |
| @@ -3664,6 +3684,8 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags, | |||
| 3664 | 3684 | ||
| 3665 | spin_lock_bh(&ifp->lock); | 3685 | spin_lock_bh(&ifp->lock); |
| 3666 | was_managetempaddr = ifp->flags & IFA_F_MANAGETEMPADDR; | 3686 | was_managetempaddr = ifp->flags & IFA_F_MANAGETEMPADDR; |
| 3687 | had_prefixroute = ifp->flags & IFA_F_PERMANENT && | ||
| 3688 | !(ifp->flags & IFA_F_NOPREFIXROUTE); | ||
| 3667 | ifp->flags &= ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_NODAD | | 3689 | ifp->flags &= ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_NODAD | |
| 3668 | IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR | | 3690 | IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR | |
| 3669 | IFA_F_NOPREFIXROUTE); | 3691 | IFA_F_NOPREFIXROUTE); |
| @@ -3679,6 +3701,18 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags, | |||
| 3679 | if (!(ifa_flags & IFA_F_NOPREFIXROUTE)) { | 3701 | if (!(ifa_flags & IFA_F_NOPREFIXROUTE)) { |
| 3680 | addrconf_prefix_route(&ifp->addr, ifp->prefix_len, ifp->idev->dev, | 3702 | addrconf_prefix_route(&ifp->addr, ifp->prefix_len, ifp->idev->dev, |
| 3681 | expires, flags); | 3703 | expires, flags); |
| 3704 | } else if (had_prefixroute) { | ||
| 3705 | enum cleanup_prefix_rt_t action; | ||
| 3706 | unsigned long rt_expires; | ||
| 3707 | |||
| 3708 | write_lock_bh(&ifp->idev->lock); | ||
| 3709 | action = check_cleanup_prefix_route(ifp, &rt_expires); | ||
| 3710 | write_unlock_bh(&ifp->idev->lock); | ||
| 3711 | |||
| 3712 | if (action != CLEANUP_PREFIX_RT_NOP) { | ||
| 3713 | cleanup_prefix_route(ifp, rt_expires, | ||
| 3714 | action == CLEANUP_PREFIX_RT_DEL); | ||
| 3715 | } | ||
| 3682 | } | 3716 | } |
| 3683 | 3717 | ||
| 3684 | if (was_managetempaddr || ifp->flags & IFA_F_MANAGETEMPADDR) { | 3718 | if (was_managetempaddr || ifp->flags & IFA_F_MANAGETEMPADDR) { |
