aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/addrconf.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/addrconf.c')
-rw-r--r--net/ipv6/addrconf.c72
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
2871static 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
2888static 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
2871static int 2894static int
2872inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) 2895inet6_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,
2945static int 2968static int
2946inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) 2969inet6_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 */