]> Pileus Git - ~andy/linux/blobdiff - net/ipv6/addrconf.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[~andy/linux] / net / ipv6 / addrconf.c
index 4b6b720971b9294910280c6ebd9d46a34ea07e35..f91e107d5f887cf57309d4c17e91994d4c85c644 100644 (file)
@@ -442,6 +442,8 @@ static int inet6_netconf_msgsize_devconf(int type)
        if (type == -1 || type == NETCONFA_MC_FORWARDING)
                size += nla_total_size(4);
 #endif
+       if (type == -1 || type == NETCONFA_PROXY_NEIGH)
+               size += nla_total_size(4);
 
        return size;
 }
@@ -475,6 +477,10 @@ static int inet6_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
                        devconf->mc_forwarding) < 0)
                goto nla_put_failure;
 #endif
+       if ((type == -1 || type == NETCONFA_PROXY_NEIGH) &&
+           nla_put_s32(skb, NETCONFA_PROXY_NEIGH, devconf->proxy_ndp) < 0)
+               goto nla_put_failure;
+
        return nlmsg_end(skb, nlh);
 
 nla_put_failure:
@@ -509,6 +515,7 @@ errout:
 static const struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = {
        [NETCONFA_IFINDEX]      = { .len = sizeof(int) },
        [NETCONFA_FORWARDING]   = { .len = sizeof(int) },
+       [NETCONFA_PROXY_NEIGH]  = { .len = sizeof(int) },
 };
 
 static int inet6_netconf_get_devconf(struct sk_buff *in_skb,
@@ -834,6 +841,8 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
                goto out;
        }
 
+       neigh_parms_data_state_setall(idev->nd_parms);
+
        ifa->addr = *addr;
        if (peer_addr)
                ifa->peer_addr = *peer_addr;
@@ -891,15 +900,95 @@ out:
        goto out2;
 }
 
+enum cleanup_prefix_rt_t {
+       CLEANUP_PREFIX_RT_NOP,    /* no cleanup action for prefix route */
+       CLEANUP_PREFIX_RT_DEL,    /* delete the prefix route */
+       CLEANUP_PREFIX_RT_EXPIRE, /* update the lifetime of the prefix route */
+};
+
+/*
+ * Check, whether the prefix for ifp would still need a prefix route
+ * after deleting ifp. The function returns one of the CLEANUP_PREFIX_RT_*
+ * constants.
+ *
+ * 1) we don't purge prefix if address was not permanent.
+ *    prefix is managed by its own lifetime.
+ * 2) we also don't purge, if the address was IFA_F_NOPREFIXROUTE.
+ * 3) if there are no addresses, delete prefix.
+ * 4) if there are still other permanent address(es),
+ *    corresponding prefix is still permanent.
+ * 5) if there are still other addresses with IFA_F_NOPREFIXROUTE,
+ *    don't purge the prefix, assume user space is managing it.
+ * 6) otherwise, update prefix lifetime to the
+ *    longest valid lifetime among the corresponding
+ *    addresses on the device.
+ *    Note: subsequent RA will update lifetime.
+ **/
+static enum cleanup_prefix_rt_t
+check_cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long *expires)
+{
+       struct inet6_ifaddr *ifa;
+       struct inet6_dev *idev = ifp->idev;
+       unsigned long lifetime;
+       enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_DEL;
+
+       *expires = jiffies;
+
+       list_for_each_entry(ifa, &idev->addr_list, if_list) {
+               if (ifa == ifp)
+                       continue;
+               if (!ipv6_prefix_equal(&ifa->addr, &ifp->addr,
+                                      ifp->prefix_len))
+                       continue;
+               if (ifa->flags & (IFA_F_PERMANENT | IFA_F_NOPREFIXROUTE))
+                       return CLEANUP_PREFIX_RT_NOP;
+
+               action = CLEANUP_PREFIX_RT_EXPIRE;
+
+               spin_lock(&ifa->lock);
+
+               lifetime = addrconf_timeout_fixup(ifa->valid_lft, HZ);
+               /*
+                * Note: Because this address is
+                * not permanent, lifetime <
+                * LONG_MAX / HZ here.
+                */
+               if (time_before(*expires, ifa->tstamp + lifetime * HZ))
+                       *expires = ifa->tstamp + lifetime * HZ;
+               spin_unlock(&ifa->lock);
+       }
+
+       return action;
+}
+
+static void
+cleanup_prefix_route(struct inet6_ifaddr *ifp, unsigned long expires, bool del_rt)
+{
+       struct rt6_info *rt;
+
+       rt = addrconf_get_prefix_route(&ifp->addr,
+                                      ifp->prefix_len,
+                                      ifp->idev->dev,
+                                      0, RTF_GATEWAY | RTF_DEFAULT);
+       if (rt) {
+               if (del_rt)
+                       ip6_del_rt(rt);
+               else {
+                       if (!(rt->rt6i_flags & RTF_EXPIRES))
+                               rt6_set_expires(rt, expires);
+                       ip6_rt_put(rt);
+               }
+       }
+}
+
+
 /* This function wants to get referenced ifp and releases it before return */
 
 static void ipv6_del_addr(struct inet6_ifaddr *ifp)
 {
-       struct inet6_ifaddr *ifa, *ifn;
-       struct inet6_dev *idev = ifp->idev;
        int state;
-       int deleted = 0, onlink = 0;
-       unsigned long expires = jiffies;
+       enum cleanup_prefix_rt_t action = CLEANUP_PREFIX_RT_NOP;
+       unsigned long expires;
 
        spin_lock_bh(&ifp->state_lock);
        state = ifp->state;
@@ -913,7 +1002,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
        hlist_del_init_rcu(&ifp->addr_lst);
        spin_unlock_bh(&addrconf_hash_lock);
 
-       write_lock_bh(&idev->lock);
+       write_lock_bh(&ifp->idev->lock);
 
        if (ifp->flags&IFA_F_TEMPORARY) {
                list_del(&ifp->tmp_list);
@@ -924,45 +1013,13 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
                __in6_ifa_put(ifp);
        }
 
-       list_for_each_entry_safe(ifa, ifn, &idev->addr_list, if_list) {
-               if (ifa == ifp) {
-                       list_del_init(&ifp->if_list);
-                       __in6_ifa_put(ifp);
+       if (ifp->flags & IFA_F_PERMANENT && !(ifp->flags & IFA_F_NOPREFIXROUTE))
+               action = check_cleanup_prefix_route(ifp, &expires);
 
-                       if (!(ifp->flags & IFA_F_PERMANENT) || onlink > 0)
-                               break;
-                       deleted = 1;
-                       continue;
-               } else if (ifp->flags & IFA_F_PERMANENT) {
-                       if (ipv6_prefix_equal(&ifa->addr, &ifp->addr,
-                                             ifp->prefix_len)) {
-                               if (ifa->flags & IFA_F_PERMANENT) {
-                                       onlink = 1;
-                                       if (deleted)
-                                               break;
-                               } else {
-                                       unsigned long lifetime;
-
-                                       if (!onlink)
-                                               onlink = -1;
-
-                                       spin_lock(&ifa->lock);
-
-                                       lifetime = addrconf_timeout_fixup(ifa->valid_lft, HZ);
-                                       /*
-                                        * Note: Because this address is
-                                        * not permanent, lifetime <
-                                        * LONG_MAX / HZ here.
-                                        */
-                                       if (time_before(expires,
-                                                       ifa->tstamp + lifetime * HZ))
-                                               expires = ifa->tstamp + lifetime * HZ;
-                                       spin_unlock(&ifa->lock);
-                               }
-                       }
-               }
-       }
-       write_unlock_bh(&idev->lock);
+       list_del_init(&ifp->if_list);
+       __in6_ifa_put(ifp);
+
+       write_unlock_bh(&ifp->idev->lock);
 
        addrconf_del_dad_timer(ifp);
 
@@ -970,41 +1027,9 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
 
        inet6addr_notifier_call_chain(NETDEV_DOWN, ifp);
 
-       /*
-        * Purge or update corresponding prefix
-        *
-        * 1) we don't purge prefix here if address was not permanent.
-        *    prefix is managed by its own lifetime.
-        * 2) if there're no addresses, delete prefix.
-        * 3) if there're still other permanent address(es),
-        *    corresponding prefix is still permanent.
-        * 4) otherwise, update prefix lifetime to the
-        *    longest valid lifetime among the corresponding
-        *    addresses on the device.
-        *    Note: subsequent RA will update lifetime.
-        *
-        * --yoshfuji
-        */
-       if ((ifp->flags & IFA_F_PERMANENT) && onlink < 1) {
-               struct in6_addr prefix;
-               struct rt6_info *rt;
-
-               ipv6_addr_prefix(&prefix, &ifp->addr, ifp->prefix_len);
-
-               rt = addrconf_get_prefix_route(&prefix,
-                                              ifp->prefix_len,
-                                              ifp->idev->dev,
-                                              0, RTF_GATEWAY | RTF_DEFAULT);
-
-               if (rt) {
-                       if (onlink == 0) {
-                               ip6_del_rt(rt);
-                               rt = NULL;
-                       } else if (!(rt->rt6i_flags & RTF_EXPIRES)) {
-                               rt6_set_expires(rt, expires);
-                       }
-               }
-               ip6_rt_put(rt);
+       if (action != CLEANUP_PREFIX_RT_NOP) {
+               cleanup_prefix_route(ifp, expires,
+                       action == CLEANUP_PREFIX_RT_DEL);
        }
 
        /* clean up prefsrc entries */
@@ -1024,7 +1049,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
        u32 addr_flags;
        unsigned long now = jiffies;
 
-       write_lock(&idev->lock);
+       write_lock_bh(&idev->lock);
        if (ift) {
                spin_lock_bh(&ift->lock);
                memcpy(&addr.s6_addr[8], &ift->addr.s6_addr[8], 8);
@@ -1036,7 +1061,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
 retry:
        in6_dev_hold(idev);
        if (idev->cnf.use_tempaddr <= 0) {
-               write_unlock(&idev->lock);
+               write_unlock_bh(&idev->lock);
                pr_info("%s: use_tempaddr is disabled\n", __func__);
                in6_dev_put(idev);
                ret = -1;
@@ -1046,7 +1071,7 @@ retry:
        if (ifp->regen_count++ >= idev->cnf.regen_max_retry) {
                idev->cnf.use_tempaddr = -1;    /*XXX*/
                spin_unlock_bh(&ifp->lock);
-               write_unlock(&idev->lock);
+               write_unlock_bh(&idev->lock);
                pr_warn("%s: regeneration time exceeded - disabled temporary address support\n",
                        __func__);
                in6_dev_put(idev);
@@ -1071,8 +1096,8 @@ retry:
 
        regen_advance = idev->cnf.regen_max_retry *
                        idev->cnf.dad_transmits *
-                       idev->nd_parms->retrans_time / HZ;
-       write_unlock(&idev->lock);
+                       NEIGH_VAR(idev->nd_parms, RETRANS_TIME) / HZ;
+       write_unlock_bh(&idev->lock);
 
        /* A temporary address is created only if this calculated Preferred
         * Lifetime is greater than REGEN_ADVANCE time units.  In particular,
@@ -1099,7 +1124,7 @@ retry:
                in6_dev_put(idev);
                pr_info("%s: retry temporary address regeneration\n", __func__);
                tmpaddr = &addr;
-               write_lock(&idev->lock);
+               write_lock_bh(&idev->lock);
                goto retry;
        }
 
@@ -1200,7 +1225,7 @@ static int ipv6_get_saddr_eval(struct net *net,
                 *       |             d is scope of the destination.
                 *  B-d  |  \
                 *       |   \      <- smaller scope is better if
-                *  B-15 |    \        if scope is enough for destinaion.
+                *  B-15 |    \        if scope is enough for destination.
                 *       |             ret = B - scope (-1 <= scope >= d <= 15).
                 * d-C-1 | /
                 *       |/         <- greater is better
@@ -1407,7 +1432,7 @@ try_nextdev:
 EXPORT_SYMBOL(ipv6_dev_get_saddr);
 
 int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr,
-                     unsigned char banned_flags)
+                     u32 banned_flags)
 {
        struct inet6_ifaddr *ifp;
        int err = -EADDRNOTAVAIL;
@@ -1424,7 +1449,7 @@ int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr,
 }
 
 int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
-                   unsigned char banned_flags)
+                   u32 banned_flags)
 {
        struct inet6_dev *idev;
        int err = -EADDRNOTAVAIL;
@@ -1816,6 +1841,7 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
                return addrconf_ifid_sit(eui, dev);
        case ARPHRD_IPGRE:
                return addrconf_ifid_gre(eui, dev);
+       case ARPHRD_6LOWPAN:
        case ARPHRD_IEEE802154:
                return addrconf_ifid_eui64(eui, dev);
        case ARPHRD_IEEE1394:
@@ -1888,7 +1914,8 @@ static void ipv6_regen_rndid(unsigned long data)
 
        expires = jiffies +
                idev->cnf.temp_prefered_lft * HZ -
-               idev->cnf.regen_max_retry * idev->cnf.dad_transmits * idev->nd_parms->retrans_time -
+               idev->cnf.regen_max_retry * idev->cnf.dad_transmits *
+               NEIGH_VAR(idev->nd_parms, RETRANS_TIME) -
                idev->cnf.max_desync_factor * HZ;
        if (time_before(expires, jiffies)) {
                pr_warn("%s: too short regeneration interval; timer disabled for %s\n",
@@ -2016,6 +2043,73 @@ static struct inet6_dev *addrconf_add_dev(struct net_device *dev)
        return idev;
 }
 
+static void manage_tempaddrs(struct inet6_dev *idev,
+                            struct inet6_ifaddr *ifp,
+                            __u32 valid_lft, __u32 prefered_lft,
+                            bool create, unsigned long now)
+{
+       u32 flags;
+       struct inet6_ifaddr *ift;
+
+       read_lock_bh(&idev->lock);
+       /* update all temporary addresses in the list */
+       list_for_each_entry(ift, &idev->tempaddr_list, tmp_list) {
+               int age, max_valid, max_prefered;
+
+               if (ifp != ift->ifpub)
+                       continue;
+
+               /* RFC 4941 section 3.3:
+                * If a received option will extend the lifetime of a public
+                * address, the lifetimes of temporary addresses should
+                * be extended, subject to the overall constraint that no
+                * temporary addresses should ever remain "valid" or "preferred"
+                * for a time longer than (TEMP_VALID_LIFETIME) or
+                * (TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR), respectively.
+                */
+               age = (now - ift->cstamp) / HZ;
+               max_valid = idev->cnf.temp_valid_lft - age;
+               if (max_valid < 0)
+                       max_valid = 0;
+
+               max_prefered = idev->cnf.temp_prefered_lft -
+                              idev->cnf.max_desync_factor - age;
+               if (max_prefered < 0)
+                       max_prefered = 0;
+
+               if (valid_lft > max_valid)
+                       valid_lft = max_valid;
+
+               if (prefered_lft > max_prefered)
+                       prefered_lft = max_prefered;
+
+               spin_lock(&ift->lock);
+               flags = ift->flags;
+               ift->valid_lft = valid_lft;
+               ift->prefered_lft = prefered_lft;
+               ift->tstamp = now;
+               if (prefered_lft > 0)
+                       ift->flags &= ~IFA_F_DEPRECATED;
+
+               spin_unlock(&ift->lock);
+               if (!(flags&IFA_F_TENTATIVE))
+                       ipv6_ifa_notify(0, ift);
+       }
+
+       if ((create || list_empty(&idev->tempaddr_list)) &&
+           idev->cnf.use_tempaddr > 0) {
+               /* When a new public address is created as described
+                * in [ADDRCONF], also create a new temporary address.
+                * Also create a temporary address if it's enabled but
+                * no temporary address currently exists.
+                */
+               read_unlock_bh(&idev->lock);
+               ipv6_create_tempaddr(ifp, NULL);
+       } else {
+               read_unlock_bh(&idev->lock);
+       }
+}
+
 void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
 {
        struct prefix_info *pinfo;
@@ -2170,6 +2264,7 @@ ok:
                                return;
                        }
 
+                       ifp->flags |= IFA_F_MANAGETEMPADDR;
                        update_lft = 0;
                        create = 1;
                        ifp->cstamp = jiffies;
@@ -2178,9 +2273,8 @@ ok:
                }
 
                if (ifp) {
-                       int flags;
+                       u32 flags;
                        unsigned long now;
-                       struct inet6_ifaddr *ift;
                        u32 stored_lft;
 
                        /* update lifetime (RFC2462 5.5.3 e) */
@@ -2221,70 +2315,8 @@ ok:
                        } else
                                spin_unlock(&ifp->lock);
 
-                       read_lock_bh(&in6_dev->lock);
-                       /* update all temporary addresses in the list */
-                       list_for_each_entry(ift, &in6_dev->tempaddr_list,
-                                           tmp_list) {
-                               int age, max_valid, max_prefered;
-
-                               if (ifp != ift->ifpub)
-                                       continue;
-
-                               /*
-                                * RFC 4941 section 3.3:
-                                * If a received option will extend the lifetime
-                                * of a public address, the lifetimes of
-                                * temporary addresses should be extended,
-                                * subject to the overall constraint that no
-                                * temporary addresses should ever remain
-                                * "valid" or "preferred" for a time longer than
-                                * (TEMP_VALID_LIFETIME) or
-                                * (TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR),
-                                * respectively.
-                                */
-                               age = (now - ift->cstamp) / HZ;
-                               max_valid = in6_dev->cnf.temp_valid_lft - age;
-                               if (max_valid < 0)
-                                       max_valid = 0;
-
-                               max_prefered = in6_dev->cnf.temp_prefered_lft -
-                                              in6_dev->cnf.max_desync_factor -
-                                              age;
-                               if (max_prefered < 0)
-                                       max_prefered = 0;
-
-                               if (valid_lft > max_valid)
-                                       valid_lft = max_valid;
-
-                               if (prefered_lft > max_prefered)
-                                       prefered_lft = max_prefered;
-
-                               spin_lock(&ift->lock);
-                               flags = ift->flags;
-                               ift->valid_lft = valid_lft;
-                               ift->prefered_lft = prefered_lft;
-                               ift->tstamp = now;
-                               if (prefered_lft > 0)
-                                       ift->flags &= ~IFA_F_DEPRECATED;
-
-                               spin_unlock(&ift->lock);
-                               if (!(flags&IFA_F_TENTATIVE))
-                                       ipv6_ifa_notify(0, ift);
-                       }
-
-                       if ((create || list_empty(&in6_dev->tempaddr_list)) && in6_dev->cnf.use_tempaddr > 0) {
-                               /*
-                                * When a new public address is created as
-                                * described in [ADDRCONF], also create a new
-                                * temporary address. Also create a temporary
-                                * address if it's enabled but no temporary
-                                * address currently exists.
-                                */
-                               read_unlock_bh(&in6_dev->lock);
-                               ipv6_create_tempaddr(ifp, NULL);
-                       } else {
-                               read_unlock_bh(&in6_dev->lock);
-                       }
+                       manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft,
+                                        create, now);
 
                        in6_ifa_put(ifp);
                        addrconf_verify(0);
@@ -2363,10 +2395,11 @@ err_exit:
 /*
  *     Manual configuration of address on an interface
  */
-static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *pfx,
+static int inet6_addr_add(struct net *net, int ifindex,
+                         const struct in6_addr *pfx,
                          const struct in6_addr *peer_pfx,
-                         unsigned int plen, __u8 ifa_flags, __u32 prefered_lft,
-                         __u32 valid_lft)
+                         unsigned int plen, __u32 ifa_flags,
+                         __u32 prefered_lft, __u32 valid_lft)
 {
        struct inet6_ifaddr *ifp;
        struct inet6_dev *idev;
@@ -2385,6 +2418,9 @@ static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *p
        if (!valid_lft || prefered_lft > valid_lft)
                return -EINVAL;
 
+       if (ifa_flags & IFA_F_MANAGETEMPADDR && plen != 64)
+               return -EINVAL;
+
        dev = __dev_get_by_index(net, ifindex);
        if (!dev)
                return -ENODEV;
@@ -2417,14 +2453,20 @@ static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *p
                            valid_lft, prefered_lft);
 
        if (!IS_ERR(ifp)) {
-               addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev,
-                                     expires, flags);
+               if (!(ifa_flags & IFA_F_NOPREFIXROUTE)) {
+                       addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev,
+                                             expires, flags);
+               }
+
                /*
                 * Note that section 3.1 of RFC 4429 indicates
                 * that the Optimistic flag should not be set for
                 * manually configured addresses
                 */
                addrconf_dad_start(ifp);
+               if (ifa_flags & IFA_F_MANAGETEMPADDR)
+                       manage_tempaddrs(idev, ifp, valid_lft, prefered_lft,
+                                        true, jiffies);
                in6_ifa_put(ifp);
                addrconf_verify(0);
                return 0;
@@ -2660,7 +2702,8 @@ static void addrconf_dev_config(struct net_device *dev)
            (dev->type != ARPHRD_INFINIBAND) &&
            (dev->type != ARPHRD_IEEE802154) &&
            (dev->type != ARPHRD_IEEE1394) &&
-           (dev->type != ARPHRD_TUNNEL6)) {
+           (dev->type != ARPHRD_TUNNEL6) &&
+           (dev->type != ARPHRD_6LOWPAN)) {
                /* Alas, we support only Ethernet autoconfiguration. */
                return;
        }
@@ -2857,7 +2900,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                }
 
                /*
-                * MTU falled under IPV6_MIN_MTU.
+                * if MTU under IPV6_MIN_MTU.
                 * Stop IPv6 on this interface.
                 */
 
@@ -3083,7 +3126,7 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp)
        if (ifp->flags & IFA_F_OPTIMISTIC)
                rand_num = 0;
        else
-               rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+               rand_num = prandom_u32() % (idev->cnf.rtr_solicit_delay ? : 1);
 
        ifp->dad_probes = idev->cnf.dad_transmits;
        addrconf_mod_dad_timer(ifp, rand_num);
@@ -3096,7 +3139,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp)
 
        addrconf_join_solict(dev, &ifp->addr);
 
-       net_srandom(ifp->addr.s6_addr32[3]);
+       prandom_seed((__force u32) ifp->addr.s6_addr32[3]);
 
        read_lock_bh(&idev->lock);
        spin_lock(&ifp->lock);
@@ -3178,7 +3221,8 @@ static void addrconf_dad_timer(unsigned long data)
        }
 
        ifp->dad_probes--;
-       addrconf_mod_dad_timer(ifp, ifp->idev->nd_parms->retrans_time);
+       addrconf_mod_dad_timer(ifp,
+                              NEIGH_VAR(ifp->idev->nd_parms, RETRANS_TIME));
        spin_unlock(&ifp->lock);
        write_unlock(&idev->lock);
 
@@ -3371,7 +3415,7 @@ static int if6_seq_show(struct seq_file *seq, void *v)
                   ifp->idev->dev->ifindex,
                   ifp->prefix_len,
                   ifp->scope,
-                  ifp->flags,
+                  (u8) ifp->flags,
                   ifp->idev->dev->name);
        return 0;
 }
@@ -3518,7 +3562,7 @@ restart:
                                   !(ifp->flags&IFA_F_TENTATIVE)) {
                                unsigned long regen_advance = ifp->idev->cnf.regen_max_retry *
                                        ifp->idev->cnf.dad_transmits *
-                                       ifp->idev->nd_parms->retrans_time / HZ;
+                                       NEIGH_VAR(ifp->idev->nd_parms, RETRANS_TIME) / HZ;
 
                                if (age >= ifp->prefered_lft - regen_advance) {
                                        struct inet6_ifaddr *ifpub = ifp->ifpub;
@@ -3593,6 +3637,7 @@ static const struct nla_policy ifa_ipv6_policy[IFA_MAX+1] = {
        [IFA_ADDRESS]           = { .len = sizeof(struct in6_addr) },
        [IFA_LOCAL]             = { .len = sizeof(struct in6_addr) },
        [IFA_CACHEINFO]         = { .len = sizeof(struct ifa_cacheinfo) },
+       [IFA_FLAGS]             = { .len = sizeof(u32) },
 };
 
 static int
@@ -3616,16 +3661,22 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
        return inet6_addr_del(net, ifm->ifa_index, pfx, ifm->ifa_prefixlen);
 }
 
-static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
+static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags,
                             u32 prefered_lft, u32 valid_lft)
 {
        u32 flags;
        clock_t expires;
        unsigned long timeout;
+       bool was_managetempaddr;
+       bool had_prefixroute;
 
        if (!valid_lft || (prefered_lft > valid_lft))
                return -EINVAL;
 
+       if (ifa_flags & IFA_F_MANAGETEMPADDR &&
+           (ifp->flags & IFA_F_TEMPORARY || ifp->prefix_len != 64))
+               return -EINVAL;
+
        timeout = addrconf_timeout_fixup(valid_lft, HZ);
        if (addrconf_finite_timeout(timeout)) {
                expires = jiffies_to_clock_t(timeout * HZ);
@@ -3645,7 +3696,13 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
        }
 
        spin_lock_bh(&ifp->lock);
-       ifp->flags = (ifp->flags & ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_NODAD | IFA_F_HOMEADDRESS)) | ifa_flags;
+       was_managetempaddr = ifp->flags & IFA_F_MANAGETEMPADDR;
+       had_prefixroute = ifp->flags & IFA_F_PERMANENT &&
+                         !(ifp->flags & IFA_F_NOPREFIXROUTE);
+       ifp->flags &= ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_NODAD |
+                       IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR |
+                       IFA_F_NOPREFIXROUTE);
+       ifp->flags |= ifa_flags;
        ifp->tstamp = jiffies;
        ifp->valid_lft = valid_lft;
        ifp->prefered_lft = prefered_lft;
@@ -3654,8 +3711,30 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
        if (!(ifp->flags&IFA_F_TENTATIVE))
                ipv6_ifa_notify(0, ifp);
 
-       addrconf_prefix_route(&ifp->addr, ifp->prefix_len, ifp->idev->dev,
-                             expires, flags);
+       if (!(ifa_flags & IFA_F_NOPREFIXROUTE)) {
+               addrconf_prefix_route(&ifp->addr, ifp->prefix_len, ifp->idev->dev,
+                                     expires, flags);
+       } else if (had_prefixroute) {
+               enum cleanup_prefix_rt_t action;
+               unsigned long rt_expires;
+
+               write_lock_bh(&ifp->idev->lock);
+               action = check_cleanup_prefix_route(ifp, &rt_expires);
+               write_unlock_bh(&ifp->idev->lock);
+
+               if (action != CLEANUP_PREFIX_RT_NOP) {
+                       cleanup_prefix_route(ifp, rt_expires,
+                               action == CLEANUP_PREFIX_RT_DEL);
+               }
+       }
+
+       if (was_managetempaddr || ifp->flags & IFA_F_MANAGETEMPADDR) {
+               if (was_managetempaddr && !(ifp->flags & IFA_F_MANAGETEMPADDR))
+                       valid_lft = prefered_lft = 0;
+               manage_tempaddrs(ifp->idev, ifp, valid_lft, prefered_lft,
+                                !was_managetempaddr, jiffies);
+       }
+
        addrconf_verify(0);
 
        return 0;
@@ -3671,7 +3750,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
        struct inet6_ifaddr *ifa;
        struct net_device *dev;
        u32 valid_lft = INFINITY_LIFE_TIME, preferred_lft = INFINITY_LIFE_TIME;
-       u8 ifa_flags;
+       u32 ifa_flags;
        int err;
 
        err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy);
@@ -3698,14 +3777,17 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
        if (dev == NULL)
                return -ENODEV;
 
+       ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifm->ifa_flags;
+
        /* We ignore other flags so far. */
-       ifa_flags = ifm->ifa_flags & (IFA_F_NODAD | IFA_F_HOMEADDRESS);
+       ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR |
+                    IFA_F_NOPREFIXROUTE;
 
        ifa = ipv6_get_ifaddr(net, pfx, dev, 1);
        if (ifa == NULL) {
                /*
                 * It would be best to check for !NLM_F_CREATE here but
-                * userspace alreay relies on not having to provide this.
+                * userspace already relies on not having to provide this.
                 */
                return inet6_addr_add(net, ifm->ifa_index, pfx, peer_pfx,
                                      ifm->ifa_prefixlen, ifa_flags,
@@ -3723,7 +3805,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
        return err;
 }
 
-static void put_ifaddrmsg(struct nlmsghdr *nlh, u8 prefixlen, u8 flags,
+static void put_ifaddrmsg(struct nlmsghdr *nlh, u8 prefixlen, u32 flags,
                          u8 scope, int ifindex)
 {
        struct ifaddrmsg *ifm;
@@ -3766,7 +3848,8 @@ static inline int inet6_ifaddr_msgsize(void)
        return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
               + nla_total_size(16) /* IFA_LOCAL */
               + nla_total_size(16) /* IFA_ADDRESS */
-              + nla_total_size(sizeof(struct ifa_cacheinfo));
+              + nla_total_size(sizeof(struct ifa_cacheinfo))
+              + nla_total_size(4)  /* IFA_FLAGS */;
 }
 
 static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
@@ -3815,6 +3898,9 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
        if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0)
                goto error;
 
+       if (nla_put_u32(skb, IFA_FLAGS, ifa->flags) < 0)
+               goto error;
+
        return nlmsg_end(skb, nlh);
 
 error:
@@ -4218,7 +4304,7 @@ static int inet6_fill_ifla6_attrs(struct sk_buff *skb, struct inet6_dev *idev)
        ci.max_reasm_len = IPV6_MAXPLEN;
        ci.tstamp = cstamp_delta(idev->tstamp);
        ci.reachable_time = jiffies_to_msecs(idev->nd_parms->reachable_time);
-       ci.retrans_time = jiffies_to_msecs(idev->nd_parms->retrans_time);
+       ci.retrans_time = jiffies_to_msecs(NEIGH_VAR(idev->nd_parms, RETRANS_TIME));
        if (nla_put(skb, IFLA_INET6_CACHEINFO, sizeof(ci), &ci))
                goto nla_put_failure;
        nla = nla_reserve(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(s32));
@@ -4694,6 +4780,46 @@ int addrconf_sysctl_disable(struct ctl_table *ctl, int write,
        return ret;
 }
 
+static
+int addrconf_sysctl_proxy_ndp(struct ctl_table *ctl, int write,
+                             void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+       int *valp = ctl->data;
+       int ret;
+       int old, new;
+
+       old = *valp;
+       ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
+       new = *valp;
+
+       if (write && old != new) {
+               struct net *net = ctl->extra2;
+
+               if (!rtnl_trylock())
+                       return restart_syscall();
+
+               if (valp == &net->ipv6.devconf_dflt->proxy_ndp)
+                       inet6_netconf_notify_devconf(net, NETCONFA_PROXY_NEIGH,
+                                                    NETCONFA_IFINDEX_DEFAULT,
+                                                    net->ipv6.devconf_dflt);
+               else if (valp == &net->ipv6.devconf_all->proxy_ndp)
+                       inet6_netconf_notify_devconf(net, NETCONFA_PROXY_NEIGH,
+                                                    NETCONFA_IFINDEX_ALL,
+                                                    net->ipv6.devconf_all);
+               else {
+                       struct inet6_dev *idev = ctl->extra1;
+
+                       inet6_netconf_notify_devconf(net, NETCONFA_PROXY_NEIGH,
+                                                    idev->dev->ifindex,
+                                                    &idev->cnf);
+               }
+               rtnl_unlock();
+       }
+
+       return ret;
+}
+
+
 static struct addrconf_sysctl_table
 {
        struct ctl_table_header *sysctl_header;
@@ -4880,7 +5006,7 @@ static struct addrconf_sysctl_table
                        .data           = &ipv6_devconf.proxy_ndp,
                        .maxlen         = sizeof(int),
                        .mode           = 0644,
-                       .proc_handler   = proc_dointvec,
+                       .proc_handler   = addrconf_sysctl_proxy_ndp,
                },
                {
                        .procname       = "accept_source_route",
@@ -4996,7 +5122,7 @@ static void __addrconf_sysctl_unregister(struct ipv6_devconf *p)
 
 static void addrconf_sysctl_register(struct inet6_dev *idev)
 {
-       neigh_sysctl_register(idev->dev, idev->nd_parms, "ipv6",
+       neigh_sysctl_register(idev->dev, idev->nd_parms,
                              &ndisc_ifinfo_sysctl_change);
        __addrconf_sysctl_register(dev_net(idev->dev), idev->dev->name,
                                        idev, &idev->cnf);
@@ -5129,9 +5255,7 @@ int __init addrconf_init(void)
 
        addrconf_verify(0);
 
-       err = rtnl_af_register(&inet6_ops);
-       if (err < 0)
-               goto errout_af;
+       rtnl_af_register(&inet6_ops);
 
        err = __rtnl_register(PF_INET6, RTM_GETLINK, NULL, inet6_dump_ifinfo,
                              NULL);
@@ -5155,7 +5279,6 @@ int __init addrconf_init(void)
        return 0;
 errout:
        rtnl_af_unregister(&inet6_ops);
-errout_af:
        unregister_netdevice_notifier(&ipv6_dev_notf);
 errlo:
        unregister_pernet_subsys(&addrconf_ops);