diff options
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r-- | net/ipv6/addrconf.c | 199 |
1 files changed, 188 insertions, 11 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index c250d0af10d7..0c5042e7380d 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -508,6 +508,26 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp) | |||
508 | kfree(ifp); | 508 | kfree(ifp); |
509 | } | 509 | } |
510 | 510 | ||
511 | static void | ||
512 | ipv6_link_dev_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp) | ||
513 | { | ||
514 | struct inet6_ifaddr *ifa, **ifap; | ||
515 | int ifp_scope = ipv6_addr_src_scope(&ifp->addr); | ||
516 | |||
517 | /* | ||
518 | * Each device address list is sorted in order of scope - | ||
519 | * global before linklocal. | ||
520 | */ | ||
521 | for (ifap = &idev->addr_list; (ifa = *ifap) != NULL; | ||
522 | ifap = &ifa->if_next) { | ||
523 | if (ifp_scope >= ipv6_addr_src_scope(&ifa->addr)) | ||
524 | break; | ||
525 | } | ||
526 | |||
527 | ifp->if_next = *ifap; | ||
528 | *ifap = ifp; | ||
529 | } | ||
530 | |||
511 | /* On success it returns ifp with increased reference count */ | 531 | /* On success it returns ifp with increased reference count */ |
512 | 532 | ||
513 | static struct inet6_ifaddr * | 533 | static struct inet6_ifaddr * |
@@ -573,8 +593,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen, | |||
573 | 593 | ||
574 | write_lock(&idev->lock); | 594 | write_lock(&idev->lock); |
575 | /* Add to inet6_dev unicast addr list. */ | 595 | /* Add to inet6_dev unicast addr list. */ |
576 | ifa->if_next = idev->addr_list; | 596 | ipv6_link_dev_addr(idev, ifa); |
577 | idev->addr_list = ifa; | ||
578 | 597 | ||
579 | #ifdef CONFIG_IPV6_PRIVACY | 598 | #ifdef CONFIG_IPV6_PRIVACY |
580 | if (ifa->flags&IFA_F_TEMPORARY) { | 599 | if (ifa->flags&IFA_F_TEMPORARY) { |
@@ -987,7 +1006,7 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev, | |||
987 | continue; | 1006 | continue; |
988 | } else if (score.scope < hiscore.scope) { | 1007 | } else if (score.scope < hiscore.scope) { |
989 | if (score.scope < daddr_scope) | 1008 | if (score.scope < daddr_scope) |
990 | continue; | 1009 | break; /* addresses sorted by scope */ |
991 | else { | 1010 | else { |
992 | score.rule = 2; | 1011 | score.rule = 2; |
993 | goto record_it; | 1012 | goto record_it; |
@@ -1850,15 +1869,21 @@ err_exit: | |||
1850 | /* | 1869 | /* |
1851 | * Manual configuration of address on an interface | 1870 | * Manual configuration of address on an interface |
1852 | */ | 1871 | */ |
1853 | static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen) | 1872 | static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen, |
1873 | __u32 prefered_lft, __u32 valid_lft) | ||
1854 | { | 1874 | { |
1855 | struct inet6_ifaddr *ifp; | 1875 | struct inet6_ifaddr *ifp; |
1856 | struct inet6_dev *idev; | 1876 | struct inet6_dev *idev; |
1857 | struct net_device *dev; | 1877 | struct net_device *dev; |
1878 | __u8 ifa_flags = 0; | ||
1858 | int scope; | 1879 | int scope; |
1859 | 1880 | ||
1860 | ASSERT_RTNL(); | 1881 | ASSERT_RTNL(); |
1861 | 1882 | ||
1883 | /* check the lifetime */ | ||
1884 | if (!valid_lft || prefered_lft > valid_lft) | ||
1885 | return -EINVAL; | ||
1886 | |||
1862 | if ((dev = __dev_get_by_index(ifindex)) == NULL) | 1887 | if ((dev = __dev_get_by_index(ifindex)) == NULL) |
1863 | return -ENODEV; | 1888 | return -ENODEV; |
1864 | 1889 | ||
@@ -1870,10 +1895,29 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen) | |||
1870 | 1895 | ||
1871 | scope = ipv6_addr_scope(pfx); | 1896 | scope = ipv6_addr_scope(pfx); |
1872 | 1897 | ||
1873 | ifp = ipv6_add_addr(idev, pfx, plen, scope, IFA_F_PERMANENT); | 1898 | if (valid_lft == INFINITY_LIFE_TIME) |
1899 | ifa_flags |= IFA_F_PERMANENT; | ||
1900 | else if (valid_lft >= 0x7FFFFFFF/HZ) | ||
1901 | valid_lft = 0x7FFFFFFF/HZ; | ||
1902 | |||
1903 | if (prefered_lft == 0) | ||
1904 | ifa_flags |= IFA_F_DEPRECATED; | ||
1905 | else if ((prefered_lft >= 0x7FFFFFFF/HZ) && | ||
1906 | (prefered_lft != INFINITY_LIFE_TIME)) | ||
1907 | prefered_lft = 0x7FFFFFFF/HZ; | ||
1908 | |||
1909 | ifp = ipv6_add_addr(idev, pfx, plen, scope, ifa_flags); | ||
1910 | |||
1874 | if (!IS_ERR(ifp)) { | 1911 | if (!IS_ERR(ifp)) { |
1912 | spin_lock_bh(&ifp->lock); | ||
1913 | ifp->valid_lft = valid_lft; | ||
1914 | ifp->prefered_lft = prefered_lft; | ||
1915 | ifp->tstamp = jiffies; | ||
1916 | spin_unlock_bh(&ifp->lock); | ||
1917 | |||
1875 | addrconf_dad_start(ifp, 0); | 1918 | addrconf_dad_start(ifp, 0); |
1876 | in6_ifa_put(ifp); | 1919 | in6_ifa_put(ifp); |
1920 | addrconf_verify(0); | ||
1877 | return 0; | 1921 | return 0; |
1878 | } | 1922 | } |
1879 | 1923 | ||
@@ -1926,7 +1970,8 @@ int addrconf_add_ifaddr(void __user *arg) | |||
1926 | return -EFAULT; | 1970 | return -EFAULT; |
1927 | 1971 | ||
1928 | rtnl_lock(); | 1972 | rtnl_lock(); |
1929 | err = inet6_addr_add(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen); | 1973 | err = inet6_addr_add(ireq.ifr6_ifindex, &ireq.ifr6_addr, ireq.ifr6_prefixlen, |
1974 | INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); | ||
1930 | rtnl_unlock(); | 1975 | rtnl_unlock(); |
1931 | return err; | 1976 | return err; |
1932 | } | 1977 | } |
@@ -2752,12 +2797,16 @@ restart: | |||
2752 | ifp->idev->nd_parms->retrans_time / HZ; | 2797 | ifp->idev->nd_parms->retrans_time / HZ; |
2753 | #endif | 2798 | #endif |
2754 | 2799 | ||
2755 | if (age >= ifp->valid_lft) { | 2800 | if (ifp->valid_lft != INFINITY_LIFE_TIME && |
2801 | age >= ifp->valid_lft) { | ||
2756 | spin_unlock(&ifp->lock); | 2802 | spin_unlock(&ifp->lock); |
2757 | in6_ifa_hold(ifp); | 2803 | in6_ifa_hold(ifp); |
2758 | read_unlock(&addrconf_hash_lock); | 2804 | read_unlock(&addrconf_hash_lock); |
2759 | ipv6_del_addr(ifp); | 2805 | ipv6_del_addr(ifp); |
2760 | goto restart; | 2806 | goto restart; |
2807 | } else if (ifp->prefered_lft == INFINITY_LIFE_TIME) { | ||
2808 | spin_unlock(&ifp->lock); | ||
2809 | continue; | ||
2761 | } else if (age >= ifp->prefered_lft) { | 2810 | } else if (age >= ifp->prefered_lft) { |
2762 | /* jiffies - ifp->tsamp > age >= ifp->prefered_lft */ | 2811 | /* jiffies - ifp->tsamp > age >= ifp->prefered_lft */ |
2763 | int deprecate = 0; | 2812 | int deprecate = 0; |
@@ -2834,7 +2883,8 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | |||
2834 | pfx = RTA_DATA(rta[IFA_ADDRESS-1]); | 2883 | pfx = RTA_DATA(rta[IFA_ADDRESS-1]); |
2835 | } | 2884 | } |
2836 | if (rta[IFA_LOCAL-1]) { | 2885 | if (rta[IFA_LOCAL-1]) { |
2837 | if (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx))) | 2886 | if (RTA_PAYLOAD(rta[IFA_LOCAL-1]) < sizeof(*pfx) || |
2887 | (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx)))) | ||
2838 | return -EINVAL; | 2888 | return -EINVAL; |
2839 | pfx = RTA_DATA(rta[IFA_LOCAL-1]); | 2889 | pfx = RTA_DATA(rta[IFA_LOCAL-1]); |
2840 | } | 2890 | } |
@@ -2845,11 +2895,61 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | |||
2845 | } | 2895 | } |
2846 | 2896 | ||
2847 | static int | 2897 | static int |
2898 | inet6_addr_modify(int ifindex, struct in6_addr *pfx, | ||
2899 | __u32 prefered_lft, __u32 valid_lft) | ||
2900 | { | ||
2901 | struct inet6_ifaddr *ifp = NULL; | ||
2902 | struct net_device *dev; | ||
2903 | int ifa_flags = 0; | ||
2904 | |||
2905 | if ((dev = __dev_get_by_index(ifindex)) == NULL) | ||
2906 | return -ENODEV; | ||
2907 | |||
2908 | if (!(dev->flags&IFF_UP)) | ||
2909 | return -ENETDOWN; | ||
2910 | |||
2911 | if (!valid_lft || (prefered_lft > valid_lft)) | ||
2912 | return -EINVAL; | ||
2913 | |||
2914 | ifp = ipv6_get_ifaddr(pfx, dev, 1); | ||
2915 | if (ifp == NULL) | ||
2916 | return -ENOENT; | ||
2917 | |||
2918 | if (valid_lft == INFINITY_LIFE_TIME) | ||
2919 | ifa_flags = IFA_F_PERMANENT; | ||
2920 | else if (valid_lft >= 0x7FFFFFFF/HZ) | ||
2921 | valid_lft = 0x7FFFFFFF/HZ; | ||
2922 | |||
2923 | if (prefered_lft == 0) | ||
2924 | ifa_flags = IFA_F_DEPRECATED; | ||
2925 | else if ((prefered_lft >= 0x7FFFFFFF/HZ) && | ||
2926 | (prefered_lft != INFINITY_LIFE_TIME)) | ||
2927 | prefered_lft = 0x7FFFFFFF/HZ; | ||
2928 | |||
2929 | spin_lock_bh(&ifp->lock); | ||
2930 | ifp->flags = (ifp->flags & ~(IFA_F_DEPRECATED|IFA_F_PERMANENT)) | ifa_flags; | ||
2931 | |||
2932 | ifp->tstamp = jiffies; | ||
2933 | ifp->valid_lft = valid_lft; | ||
2934 | ifp->prefered_lft = prefered_lft; | ||
2935 | |||
2936 | spin_unlock_bh(&ifp->lock); | ||
2937 | if (!(ifp->flags&IFA_F_TENTATIVE)) | ||
2938 | ipv6_ifa_notify(0, ifp); | ||
2939 | in6_ifa_put(ifp); | ||
2940 | |||
2941 | addrconf_verify(0); | ||
2942 | |||
2943 | return 0; | ||
2944 | } | ||
2945 | |||
2946 | static int | ||
2848 | inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | 2947 | inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) |
2849 | { | 2948 | { |
2850 | struct rtattr **rta = arg; | 2949 | struct rtattr **rta = arg; |
2851 | struct ifaddrmsg *ifm = NLMSG_DATA(nlh); | 2950 | struct ifaddrmsg *ifm = NLMSG_DATA(nlh); |
2852 | struct in6_addr *pfx; | 2951 | struct in6_addr *pfx; |
2952 | __u32 valid_lft = INFINITY_LIFE_TIME, prefered_lft = INFINITY_LIFE_TIME; | ||
2853 | 2953 | ||
2854 | pfx = NULL; | 2954 | pfx = NULL; |
2855 | if (rta[IFA_ADDRESS-1]) { | 2955 | if (rta[IFA_ADDRESS-1]) { |
@@ -2858,14 +2958,34 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | |||
2858 | pfx = RTA_DATA(rta[IFA_ADDRESS-1]); | 2958 | pfx = RTA_DATA(rta[IFA_ADDRESS-1]); |
2859 | } | 2959 | } |
2860 | if (rta[IFA_LOCAL-1]) { | 2960 | if (rta[IFA_LOCAL-1]) { |
2861 | if (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx))) | 2961 | if (RTA_PAYLOAD(rta[IFA_LOCAL-1]) < sizeof(*pfx) || |
2962 | (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx)))) | ||
2862 | return -EINVAL; | 2963 | return -EINVAL; |
2863 | pfx = RTA_DATA(rta[IFA_LOCAL-1]); | 2964 | pfx = RTA_DATA(rta[IFA_LOCAL-1]); |
2864 | } | 2965 | } |
2865 | if (pfx == NULL) | 2966 | if (pfx == NULL) |
2866 | return -EINVAL; | 2967 | return -EINVAL; |
2867 | 2968 | ||
2868 | return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen); | 2969 | if (rta[IFA_CACHEINFO-1]) { |
2970 | struct ifa_cacheinfo *ci; | ||
2971 | if (RTA_PAYLOAD(rta[IFA_CACHEINFO-1]) < sizeof(*ci)) | ||
2972 | return -EINVAL; | ||
2973 | ci = RTA_DATA(rta[IFA_CACHEINFO-1]); | ||
2974 | valid_lft = ci->ifa_valid; | ||
2975 | prefered_lft = ci->ifa_prefered; | ||
2976 | } | ||
2977 | |||
2978 | if (nlh->nlmsg_flags & NLM_F_REPLACE) { | ||
2979 | int ret; | ||
2980 | ret = inet6_addr_modify(ifm->ifa_index, pfx, | ||
2981 | prefered_lft, valid_lft); | ||
2982 | if (ret == 0 || !(nlh->nlmsg_flags & NLM_F_CREATE)) | ||
2983 | return ret; | ||
2984 | } | ||
2985 | |||
2986 | return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen, | ||
2987 | prefered_lft, valid_lft); | ||
2988 | |||
2869 | } | 2989 | } |
2870 | 2990 | ||
2871 | /* Maximum length of ifa_cacheinfo attributes */ | 2991 | /* Maximum length of ifa_cacheinfo attributes */ |
@@ -3102,6 +3222,62 @@ static int inet6_dump_ifacaddr(struct sk_buff *skb, struct netlink_callback *cb) | |||
3102 | return inet6_dump_addr(skb, cb, type); | 3222 | return inet6_dump_addr(skb, cb, type); |
3103 | } | 3223 | } |
3104 | 3224 | ||
3225 | static int inet6_rtm_getaddr(struct sk_buff *in_skb, | ||
3226 | struct nlmsghdr* nlh, void *arg) | ||
3227 | { | ||
3228 | struct rtattr **rta = arg; | ||
3229 | struct ifaddrmsg *ifm = NLMSG_DATA(nlh); | ||
3230 | struct in6_addr *addr = NULL; | ||
3231 | struct net_device *dev = NULL; | ||
3232 | struct inet6_ifaddr *ifa; | ||
3233 | struct sk_buff *skb; | ||
3234 | int size = NLMSG_SPACE(sizeof(struct ifaddrmsg) + INET6_IFADDR_RTA_SPACE); | ||
3235 | int err; | ||
3236 | |||
3237 | if (rta[IFA_ADDRESS-1]) { | ||
3238 | if (RTA_PAYLOAD(rta[IFA_ADDRESS-1]) < sizeof(*addr)) | ||
3239 | return -EINVAL; | ||
3240 | addr = RTA_DATA(rta[IFA_ADDRESS-1]); | ||
3241 | } | ||
3242 | if (rta[IFA_LOCAL-1]) { | ||
3243 | if (RTA_PAYLOAD(rta[IFA_LOCAL-1]) < sizeof(*addr) || | ||
3244 | (addr && memcmp(addr, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*addr)))) | ||
3245 | return -EINVAL; | ||
3246 | addr = RTA_DATA(rta[IFA_LOCAL-1]); | ||
3247 | } | ||
3248 | if (addr == NULL) | ||
3249 | return -EINVAL; | ||
3250 | |||
3251 | if (ifm->ifa_index) | ||
3252 | dev = __dev_get_by_index(ifm->ifa_index); | ||
3253 | |||
3254 | if ((ifa = ipv6_get_ifaddr(addr, dev, 1)) == NULL) | ||
3255 | return -EADDRNOTAVAIL; | ||
3256 | |||
3257 | if ((skb = alloc_skb(size, GFP_KERNEL)) == NULL) { | ||
3258 | err = -ENOBUFS; | ||
3259 | goto out; | ||
3260 | } | ||
3261 | |||
3262 | NETLINK_CB(skb).dst_pid = NETLINK_CB(in_skb).pid; | ||
3263 | err = inet6_fill_ifaddr(skb, ifa, NETLINK_CB(in_skb).pid, | ||
3264 | nlh->nlmsg_seq, RTM_NEWADDR, 0); | ||
3265 | if (err < 0) { | ||
3266 | err = -EMSGSIZE; | ||
3267 | goto out_free; | ||
3268 | } | ||
3269 | |||
3270 | err = netlink_unicast(rtnl, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT); | ||
3271 | if (err > 0) | ||
3272 | err = 0; | ||
3273 | out: | ||
3274 | in6_ifa_put(ifa); | ||
3275 | return err; | ||
3276 | out_free: | ||
3277 | kfree_skb(skb); | ||
3278 | goto out; | ||
3279 | } | ||
3280 | |||
3105 | static void inet6_ifa_notify(int event, struct inet6_ifaddr *ifa) | 3281 | static void inet6_ifa_notify(int event, struct inet6_ifaddr *ifa) |
3106 | { | 3282 | { |
3107 | struct sk_buff *skb; | 3283 | struct sk_buff *skb; |
@@ -3344,7 +3520,8 @@ static struct rtnetlink_link inet6_rtnetlink_table[RTM_NR_MSGTYPES] = { | |||
3344 | [RTM_GETLINK - RTM_BASE] = { .dumpit = inet6_dump_ifinfo, }, | 3520 | [RTM_GETLINK - RTM_BASE] = { .dumpit = inet6_dump_ifinfo, }, |
3345 | [RTM_NEWADDR - RTM_BASE] = { .doit = inet6_rtm_newaddr, }, | 3521 | [RTM_NEWADDR - RTM_BASE] = { .doit = inet6_rtm_newaddr, }, |
3346 | [RTM_DELADDR - RTM_BASE] = { .doit = inet6_rtm_deladdr, }, | 3522 | [RTM_DELADDR - RTM_BASE] = { .doit = inet6_rtm_deladdr, }, |
3347 | [RTM_GETADDR - RTM_BASE] = { .dumpit = inet6_dump_ifaddr, }, | 3523 | [RTM_GETADDR - RTM_BASE] = { .doit = inet6_rtm_getaddr, |
3524 | .dumpit = inet6_dump_ifaddr, }, | ||
3348 | [RTM_GETMULTICAST - RTM_BASE] = { .dumpit = inet6_dump_ifmcaddr, }, | 3525 | [RTM_GETMULTICAST - RTM_BASE] = { .dumpit = inet6_dump_ifmcaddr, }, |
3349 | [RTM_GETANYCAST - RTM_BASE] = { .dumpit = inet6_dump_ifacaddr, }, | 3526 | [RTM_GETANYCAST - RTM_BASE] = { .dumpit = inet6_dump_ifacaddr, }, |
3350 | [RTM_NEWROUTE - RTM_BASE] = { .doit = inet6_rtm_newroute, }, | 3527 | [RTM_NEWROUTE - RTM_BASE] = { .doit = inet6_rtm_newroute, }, |