|
@@ -2868,6 +2868,29 @@ restart:
|
|
|
spin_unlock_bh(&addrconf_verify_lock);
|
|
|
}
|
|
|
|
|
|
+static struct in6_addr *extract_addr(struct nlattr *addr, struct nlattr *local)
|
|
|
+{
|
|
|
+ struct in6_addr *pfx = NULL;
|
|
|
+
|
|
|
+ if (addr)
|
|
|
+ pfx = nla_data(addr);
|
|
|
+
|
|
|
+ if (local) {
|
|
|
+ if (pfx && nla_memcmp(local, pfx, sizeof(*pfx)))
|
|
|
+ pfx = NULL;
|
|
|
+ else
|
|
|
+ pfx = nla_data(local);
|
|
|
+ }
|
|
|
+
|
|
|
+ return pfx;
|
|
|
+}
|
|
|
+
|
|
|
+static struct nla_policy ifa_ipv6_policy[IFA_MAX+1] __read_mostly = {
|
|
|
+ [IFA_ADDRESS] = { .len = sizeof(struct in6_addr) },
|
|
|
+ [IFA_LOCAL] = { .len = sizeof(struct in6_addr) },
|
|
|
+ [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) },
|
|
|
+};
|
|
|
+
|
|
|
static int
|
|
|
inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
|
|
|
{
|
|
@@ -2945,46 +2968,41 @@ inet6_addr_modify(int ifindex, struct in6_addr *pfx,
|
|
|
static int
|
|
|
inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
|
|
|
{
|
|
|
- struct rtattr **rta = arg;
|
|
|
- struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
|
|
|
+ struct ifaddrmsg *ifm;
|
|
|
+ struct nlattr *tb[IFA_MAX+1];
|
|
|
struct in6_addr *pfx;
|
|
|
- __u32 valid_lft = INFINITY_LIFE_TIME, prefered_lft = INFINITY_LIFE_TIME;
|
|
|
+ u32 valid_lft, preferred_lft;
|
|
|
+ int err;
|
|
|
|
|
|
- pfx = NULL;
|
|
|
- if (rta[IFA_ADDRESS-1]) {
|
|
|
- if (RTA_PAYLOAD(rta[IFA_ADDRESS-1]) < sizeof(*pfx))
|
|
|
- return -EINVAL;
|
|
|
- pfx = RTA_DATA(rta[IFA_ADDRESS-1]);
|
|
|
- }
|
|
|
- if (rta[IFA_LOCAL-1]) {
|
|
|
- if (RTA_PAYLOAD(rta[IFA_LOCAL-1]) < sizeof(*pfx) ||
|
|
|
- (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx))))
|
|
|
- return -EINVAL;
|
|
|
- pfx = RTA_DATA(rta[IFA_LOCAL-1]);
|
|
|
- }
|
|
|
+ err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ ifm = nlmsg_data(nlh);
|
|
|
+ pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL]);
|
|
|
if (pfx == NULL)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- if (rta[IFA_CACHEINFO-1]) {
|
|
|
+ if (tb[IFA_CACHEINFO]) {
|
|
|
struct ifa_cacheinfo *ci;
|
|
|
- if (RTA_PAYLOAD(rta[IFA_CACHEINFO-1]) < sizeof(*ci))
|
|
|
- return -EINVAL;
|
|
|
- ci = RTA_DATA(rta[IFA_CACHEINFO-1]);
|
|
|
+
|
|
|
+ ci = nla_data(tb[IFA_CACHEINFO]);
|
|
|
valid_lft = ci->ifa_valid;
|
|
|
- prefered_lft = ci->ifa_prefered;
|
|
|
+ preferred_lft = ci->ifa_prefered;
|
|
|
+ } else {
|
|
|
+ preferred_lft = INFINITY_LIFE_TIME;
|
|
|
+ valid_lft = INFINITY_LIFE_TIME;
|
|
|
}
|
|
|
|
|
|
if (nlh->nlmsg_flags & NLM_F_REPLACE) {
|
|
|
- int ret;
|
|
|
- ret = inet6_addr_modify(ifm->ifa_index, pfx,
|
|
|
- prefered_lft, valid_lft);
|
|
|
- if (ret == 0 || !(nlh->nlmsg_flags & NLM_F_CREATE))
|
|
|
- return ret;
|
|
|
+ err = inet6_addr_modify(ifm->ifa_index, pfx,
|
|
|
+ preferred_lft, valid_lft);
|
|
|
+ if (err == 0 || !(nlh->nlmsg_flags & NLM_F_CREATE))
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen,
|
|
|
- prefered_lft, valid_lft);
|
|
|
-
|
|
|
+ preferred_lft, valid_lft);
|
|
|
}
|
|
|
|
|
|
/* Maximum length of ifa_cacheinfo attributes */
|