diff options
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r-- | net/ipv6/addrconf.c | 72 |
1 files changed, 45 insertions, 27 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index fc9cff3426c4..52ba96a64a1f 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -2868,6 +2868,29 @@ restart: | |||
2868 | spin_unlock_bh(&addrconf_verify_lock); | 2868 | spin_unlock_bh(&addrconf_verify_lock); |
2869 | } | 2869 | } |
2870 | 2870 | ||
2871 | static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local) | ||
2872 | { | ||
2873 | struct in6_addr *pfx = NULL; | ||
2874 | |||
2875 | if (addr) | ||
2876 | pfx = nla_data(addr); | ||
2877 | |||
2878 | if (local) { | ||
2879 | if (pfx && nla_memcmp(local, pfx, sizeof(*pfx))) | ||
2880 | pfx = NULL; | ||
2881 | else | ||
2882 | pfx = nla_data(local); | ||
2883 | } | ||
2884 | |||
2885 | return pfx; | ||
2886 | } | ||
2887 | |||
2888 | static struct nla_policy ifa_ipv6_policy[IFA_MAX+1] __read_mostly = { | ||
2889 | [IFA_ADDRESS] = { .len = sizeof(struct in6_addr) }, | ||
2890 | [IFA_LOCAL] = { .len = sizeof(struct in6_addr) }, | ||
2891 | [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) }, | ||
2892 | }; | ||
2893 | |||
2871 | static int | 2894 | static int |
2872 | inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | 2895 | inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) |
2873 | { | 2896 | { |
@@ -2945,46 +2968,41 @@ inet6_addr_modify(int ifindex, struct in6_addr *pfx, | |||
2945 | static int | 2968 | static int |
2946 | inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | 2969 | inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) |
2947 | { | 2970 | { |
2948 | struct rtattr **rta = arg; | 2971 | struct ifaddrmsg *ifm; |
2949 | struct ifaddrmsg *ifm = NLMSG_DATA(nlh); | 2972 | struct nlattr *tb[IFA_MAX+1]; |
2950 | struct in6_addr *pfx; | 2973 | struct in6_addr *pfx; |
2951 | __u32 valid_lft = INFINITY_LIFE_TIME, prefered_lft = INFINITY_LIFE_TIME; | 2974 | u32 valid_lft, preferred_lft; |
2975 | int err; | ||
2952 | 2976 | ||
2953 | pfx = NULL; | 2977 | err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy); |
2954 | if (rta[IFA_ADDRESS-1]) { | 2978 | if (err < 0) |
2955 | if (RTA_PAYLOAD(rta[IFA_ADDRESS-1]) < sizeof(*pfx)) | 2979 | return err; |
2956 | return -EINVAL; | 2980 | |
2957 | pfx = RTA_DATA(rta[IFA_ADDRESS-1]); | 2981 | ifm = nlmsg_data(nlh); |
2958 | } | 2982 | pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]); |
2959 | if (rta[IFA_LOCAL-1]) { | ||
2960 | if (RTA_PAYLOAD(rta[IFA_LOCAL-1]) < sizeof(*pfx) || | ||
2961 | (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx)))) | ||
2962 | return -EINVAL; | ||
2963 | pfx = RTA_DATA(rta[IFA_LOCAL-1]); | ||
2964 | } | ||
2965 | if (pfx == NULL) | 2983 | if (pfx == NULL) |
2966 | return -EINVAL; | 2984 | return -EINVAL; |
2967 | 2985 | ||
2968 | if (rta[IFA_CACHEINFO-1]) { | 2986 | if (tb[IFA_CACHEINFO]) { |
2969 | struct ifa_cacheinfo *ci; | 2987 | struct ifa_cacheinfo *ci; |
2970 | if (RTA_PAYLOAD(rta[IFA_CACHEINFO-1]) < sizeof(*ci)) | 2988 | |
2971 | return -EINVAL; | 2989 | ci = nla_data(tb[IFA_CACHEINFO]); |
2972 | ci = RTA_DATA(rta[IFA_CACHEINFO-1]); | ||
2973 | valid_lft = ci->ifa_valid; | 2990 | valid_lft = ci->ifa_valid; |
2974 | prefered_lft = ci->ifa_prefered; | 2991 | preferred_lft = ci->ifa_prefered; |
2992 | } else { | ||
2993 | preferred_lft = INFINITY_LIFE_TIME; | ||
2994 | valid_lft = INFINITY_LIFE_TIME; | ||
2975 | } | 2995 | } |
2976 | 2996 | ||
2977 | if (nlh->nlmsg_flags & NLM_F_REPLACE) { | 2997 | if (nlh->nlmsg_flags & NLM_F_REPLACE) { |
2978 | int ret; | 2998 | err = inet6_addr_modify(ifm->ifa_index, pfx, |
2979 | ret = inet6_addr_modify(ifm->ifa_index, pfx, | 2999 | preferred_lft, valid_lft); |
2980 | prefered_lft, valid_lft); | 3000 | if (err == 0 || !(nlh->nlmsg_flags & NLM_F_CREATE)) |
2981 | if (ret == 0 || !(nlh->nlmsg_flags & NLM_F_CREATE)) | 3001 | return err; |
2982 | return ret; | ||
2983 | } | 3002 | } |
2984 | 3003 | ||
2985 | return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen, | 3004 | return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen, |
2986 | prefered_lft, valid_lft); | 3005 | preferred_lft, valid_lft); |
2987 | |||
2988 | } | 3006 | } |
2989 | 3007 | ||
2990 | /* Maximum length of ifa_cacheinfo attributes */ | 3008 | /* Maximum length of ifa_cacheinfo attributes */ |