Merge tag 'samsung-fixes-4.1-4' of https://github.com/krzk/linux into fixes
[cascardo/linux.git] / net / ipv6 / addrconf.c
index b603002..37b70e8 100644 (file)
@@ -46,6 +46,7 @@
 #include <linux/socket.h>
 #include <linux/sockios.h>
 #include <linux/net.h>
+#include <linux/inet.h>
 #include <linux/in6.h>
 #include <linux/netdevice.h>
 #include <linux/if_addr.h>
 
 #define        INFINITY_LIFE_TIME      0xFFFFFFFF
 
+#define IPV6_MAX_STRLEN \
+       sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")
+
 static inline u32 cstamp_delta(unsigned long cstamp)
 {
        return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
@@ -127,6 +131,9 @@ static void ipv6_regen_rndid(unsigned long data);
 
 static int ipv6_generate_eui64(u8 *eui, struct net_device *dev);
 static int ipv6_count_addresses(struct inet6_dev *idev);
+static int ipv6_generate_stable_address(struct in6_addr *addr,
+                                       u8 dad_count,
+                                       const struct inet6_dev *idev);
 
 /*
  *     Configured unicast address hash table
@@ -202,6 +209,9 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
        .accept_dad             = 1,
        .suppress_frag_ndisc    = 1,
        .accept_ra_mtu          = 1,
+       .stable_secret          = {
+               .initialized = false,
+       }
 };
 
 static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -240,6 +250,9 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
        .accept_dad             = 1,
        .suppress_frag_ndisc    = 1,
        .accept_ra_mtu          = 1,
+       .stable_secret          = {
+               .initialized = false,
+       },
 };
 
 /* Check if a valid qdisc is available */
@@ -321,7 +334,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
                return ERR_PTR(-EINVAL);
 
        ndev = kzalloc(sizeof(struct inet6_dev), GFP_KERNEL);
-       if (ndev == NULL)
+       if (!ndev)
                return ERR_PTR(err);
 
        rwlock_init(&ndev->lock);
@@ -333,7 +346,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
        ndev->cnf.mtu6 = dev->mtu;
        ndev->cnf.sysctl = NULL;
        ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);
-       if (ndev->nd_parms == NULL) {
+       if (!ndev->nd_parms) {
                kfree(ndev);
                return ERR_PTR(err);
        }
@@ -468,7 +481,7 @@ static int inet6_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
 
        nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
                        flags);
-       if (nlh == NULL)
+       if (!nlh)
                return -EMSGSIZE;
 
        ncm = nlmsg_data(nlh);
@@ -506,7 +519,7 @@ void inet6_netconf_notify_devconf(struct net *net, int type, int ifindex,
        int err = -ENOBUFS;
 
        skb = nlmsg_new(inet6_netconf_msgsize_devconf(type), GFP_ATOMIC);
-       if (skb == NULL)
+       if (!skb)
                goto errout;
 
        err = inet6_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
@@ -561,10 +574,10 @@ static int inet6_netconf_get_devconf(struct sk_buff *in_skb,
                break;
        default:
                dev = __dev_get_by_index(net, ifindex);
-               if (dev == NULL)
+               if (!dev)
                        goto errout;
                in6_dev = __in6_dev_get(dev);
-               if (in6_dev == NULL)
+               if (!in6_dev)
                        goto errout;
                devconf = &in6_dev->cnf;
                break;
@@ -572,7 +585,7 @@ static int inet6_netconf_get_devconf(struct sk_buff *in_skb,
 
        err = -ENOBUFS;
        skb = nlmsg_new(inet6_netconf_msgsize_devconf(-1), GFP_ATOMIC);
-       if (skb == NULL)
+       if (!skb)
                goto errout;
 
        err = inet6_netconf_fill_devconf(skb, ifindex, devconf,
@@ -841,7 +854,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
 
        ifa = kzalloc(sizeof(struct inet6_ifaddr), GFP_ATOMIC);
 
-       if (ifa == NULL) {
+       if (!ifa) {
                ADBG("ipv6_add_addr: malloc failed\n");
                err = -ENOBUFS;
                goto out;
@@ -860,7 +873,6 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
                ifa->peer_addr = *peer_addr;
 
        spin_lock_init(&ifa->lock);
-       spin_lock_init(&ifa->state_lock);
        INIT_DELAYED_WORK(&ifa->dad_work, addrconf_dad_work);
        INIT_HLIST_NODE(&ifa->addr_lst);
        ifa->scope = scope;
@@ -1003,10 +1015,10 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
 
        ASSERT_RTNL();
 
-       spin_lock_bh(&ifp->state_lock);
+       spin_lock_bh(&ifp->lock);
        state = ifp->state;
        ifp->state = INET6_IFADDR_STATE_DEAD;
-       spin_unlock_bh(&ifp->state_lock);
+       spin_unlock_bh(&ifp->lock);
 
        if (state == INET6_IFADDR_STATE_DEAD)
                goto out;
@@ -1546,7 +1558,7 @@ int ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr,
                            : ifp->flags;
                if (ipv6_addr_equal(&ifp->addr, addr) &&
                    !(ifp_flags&banned_flags) &&
-                   (dev == NULL || ifp->idev->dev == dev ||
+                   (!dev || ifp->idev->dev == dev ||
                     !(ifp->scope&(IFA_LINK|IFA_HOST) || strict))) {
                        rcu_read_unlock_bh();
                        return 1;
@@ -1568,7 +1580,7 @@ static bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr,
                if (!net_eq(dev_net(ifp->idev->dev), net))
                        continue;
                if (ipv6_addr_equal(&ifp->addr, addr)) {
-                       if (dev == NULL || ifp->idev->dev == dev)
+                       if (!dev || ifp->idev->dev == dev)
                                return true;
                }
        }
@@ -1637,7 +1649,7 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add
                if (!net_eq(dev_net(ifp->idev->dev), net))
                        continue;
                if (ipv6_addr_equal(&ifp->addr, addr)) {
-                       if (dev == NULL || ifp->idev->dev == dev ||
+                       if (!dev || ifp->idev->dev == dev ||
                            !(ifp->scope&(IFA_LINK|IFA_HOST) || strict)) {
                                result = ifp;
                                in6_ifa_hold(ifp);
@@ -1686,19 +1698,21 @@ static int addrconf_dad_end(struct inet6_ifaddr *ifp)
 {
        int err = -ENOENT;
 
-       spin_lock_bh(&ifp->state_lock);
+       spin_lock_bh(&ifp->lock);
        if (ifp->state == INET6_IFADDR_STATE_DAD) {
                ifp->state = INET6_IFADDR_STATE_POSTDAD;
                err = 0;
        }
-       spin_unlock_bh(&ifp->state_lock);
+       spin_unlock_bh(&ifp->lock);
 
        return err;
 }
 
 void addrconf_dad_failure(struct inet6_ifaddr *ifp)
 {
+       struct in6_addr addr;
        struct inet6_dev *idev = ifp->idev;
+       struct net *net = dev_net(ifp->idev->dev);
 
        if (addrconf_dad_end(ifp)) {
                in6_ifa_put(ifp);
@@ -1708,9 +1722,57 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp)
        net_info_ratelimited("%s: IPv6 duplicate address %pI6c detected!\n",
                             ifp->idev->dev->name, &ifp->addr);
 
-       if (idev->cnf.accept_dad > 1 && !idev->cnf.disable_ipv6) {
-               struct in6_addr addr;
+       spin_lock_bh(&ifp->lock);
+
+       if (ifp->flags & IFA_F_STABLE_PRIVACY) {
+               int scope = ifp->scope;
+               u32 flags = ifp->flags;
+               struct in6_addr new_addr;
+               struct inet6_ifaddr *ifp2;
+               u32 valid_lft, preferred_lft;
+               int pfxlen = ifp->prefix_len;
+               int retries = ifp->stable_privacy_retry + 1;
+
+               if (retries > net->ipv6.sysctl.idgen_retries) {
+                       net_info_ratelimited("%s: privacy stable address generation failed because of DAD conflicts!\n",
+                                            ifp->idev->dev->name);
+                       goto errdad;
+               }
+
+               new_addr = ifp->addr;
+               if (ipv6_generate_stable_address(&new_addr, retries,
+                                                idev))
+                       goto errdad;
+
+               valid_lft = ifp->valid_lft;
+               preferred_lft = ifp->prefered_lft;
+
+               spin_unlock_bh(&ifp->lock);
+
+               if (idev->cnf.max_addresses &&
+                   ipv6_count_addresses(idev) >=
+                   idev->cnf.max_addresses)
+                       goto lock_errdad;
+
+               net_info_ratelimited("%s: generating new stable privacy address because of DAD conflict\n",
+                                    ifp->idev->dev->name);
+
+               ifp2 = ipv6_add_addr(idev, &new_addr, NULL, pfxlen,
+                                    scope, flags, valid_lft,
+                                    preferred_lft);
+               if (IS_ERR(ifp2))
+                       goto lock_errdad;
+
+               spin_lock_bh(&ifp2->lock);
+               ifp2->stable_privacy_retry = retries;
+               ifp2->state = INET6_IFADDR_STATE_PREDAD;
+               spin_unlock_bh(&ifp2->lock);
 
+               addrconf_mod_dad_work(ifp2, net->ipv6.sysctl.idgen_delay);
+               in6_ifa_put(ifp2);
+lock_errdad:
+               spin_lock_bh(&ifp->lock);
+       } else if (idev->cnf.accept_dad > 1 && !idev->cnf.disable_ipv6) {
                addr.s6_addr32[0] = htonl(0xfe800000);
                addr.s6_addr32[1] = 0;
 
@@ -1724,10 +1786,10 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp)
                }
        }
 
-       spin_lock_bh(&ifp->state_lock);
+errdad:
        /* transition from _POSTDAD to _ERRDAD */
        ifp->state = INET6_IFADDR_STATE_ERRDAD;
-       spin_unlock_bh(&ifp->state_lock);
+       spin_unlock_bh(&ifp->lock);
 
        addrconf_mod_dad_work(ifp, 0);
 }
@@ -2052,7 +2114,7 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
        struct fib6_table *table;
 
        table = fib6_get_table(dev_net(dev), RT6_TABLE_PREFIX);
-       if (table == NULL)
+       if (!table)
                return NULL;
 
        read_lock_bh(&table->tb6_lock);
@@ -2186,6 +2248,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
        __u32 valid_lft;
        __u32 prefered_lft;
        int addr_type;
+       u32 addr_flags = 0;
        struct inet6_dev *in6_dev;
        struct net *net = dev_net(dev);
 
@@ -2215,7 +2278,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
 
        in6_dev = in6_dev_get(dev);
 
-       if (in6_dev == NULL) {
+       if (!in6_dev) {
                net_dbg_ratelimited("addrconf: device %s not configured\n",
                                    dev->name);
                return;
@@ -2292,6 +2355,12 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
                                       in6_dev->token.s6_addr + 8, 8);
                                read_unlock_bh(&in6_dev->lock);
                                tokenized = true;
+                       } else if (in6_dev->addr_gen_mode ==
+                                  IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
+                                  !ipv6_generate_stable_address(&addr, 0,
+                                                                in6_dev)) {
+                               addr_flags |= IFA_F_STABLE_PRIVACY;
+                               goto ok;
                        } else if (ipv6_generate_eui64(addr.s6_addr + 8, dev) &&
                                   ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {
                                in6_dev_put(in6_dev);
@@ -2308,9 +2377,8 @@ ok:
 
                ifp = ipv6_get_ifaddr(net, &addr, dev, 1);
 
-               if (ifp == NULL && valid_lft) {
+               if (!ifp && valid_lft) {
                        int max_addresses = in6_dev->cnf.max_addresses;
-                       u32 addr_flags = 0;
 
 #ifdef CONFIG_IPV6_OPTIMISTIC_DAD
                        if (in6_dev->cnf.optimistic_dad &&
@@ -2350,7 +2418,7 @@ ok:
                        u32 stored_lft;
 
                        /* update lifetime (RFC2462 5.5.3 e) */
-                       spin_lock(&ifp->lock);
+                       spin_lock_bh(&ifp->lock);
                        now = jiffies;
                        if (ifp->valid_lft > (now - ifp->tstamp) / HZ)
                                stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ;
@@ -2380,12 +2448,12 @@ ok:
                                ifp->tstamp = now;
                                flags = ifp->flags;
                                ifp->flags &= ~IFA_F_DEPRECATED;
-                               spin_unlock(&ifp->lock);
+                               spin_unlock_bh(&ifp->lock);
 
                                if (!(flags&IFA_F_TENTATIVE))
                                        ipv6_ifa_notify(0, ifp);
                        } else
-                               spin_unlock(&ifp->lock);
+                               spin_unlock_bh(&ifp->lock);
 
                        manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft,
                                         create, now);
@@ -2418,7 +2486,7 @@ int addrconf_set_dstaddr(struct net *net, void __user *arg)
        dev = __dev_get_by_index(net, ireq.ifr6_ifindex);
 
        err = -ENODEV;
-       if (dev == NULL)
+       if (!dev)
                goto err_exit;
 
 #if IS_ENABLED(CONFIG_IPV6_SIT)
@@ -2464,6 +2532,23 @@ err_exit:
        return err;
 }
 
+static int ipv6_mc_config(struct sock *sk, bool join,
+                         const struct in6_addr *addr, int ifindex)
+{
+       int ret;
+
+       ASSERT_RTNL();
+
+       lock_sock(sk);
+       if (join)
+               ret = ipv6_sock_mc_join(sk, ifindex, addr);
+       else
+               ret = ipv6_sock_mc_drop(sk, ifindex, addr);
+       release_sock(sk);
+
+       return ret;
+}
+
 /*
  *     Manual configuration of address on an interface
  */
@@ -2476,10 +2561,10 @@ static int inet6_addr_add(struct net *net, int ifindex,
        struct inet6_ifaddr *ifp;
        struct inet6_dev *idev;
        struct net_device *dev;
+       unsigned long timeout;
+       clock_t expires;
        int scope;
        u32 flags;
-       clock_t expires;
-       unsigned long timeout;
 
        ASSERT_RTNL();
 
@@ -2501,6 +2586,14 @@ static int inet6_addr_add(struct net *net, int ifindex,
        if (IS_ERR(idev))
                return PTR_ERR(idev);
 
+       if (ifa_flags & IFA_F_MCAUTOJOIN) {
+               int ret = ipv6_mc_config(net->ipv6.mc_autojoin_sk,
+                                        true, pfx, ifindex);
+
+               if (ret < 0)
+                       return ret;
+       }
+
        scope = ipv6_addr_scope(pfx);
 
        timeout = addrconf_timeout_fixup(valid_lft, HZ);
@@ -2542,6 +2635,9 @@ static int inet6_addr_add(struct net *net, int ifindex,
                in6_ifa_put(ifp);
                addrconf_verify_rtnl();
                return 0;
+       } else if (ifa_flags & IFA_F_MCAUTOJOIN) {
+               ipv6_mc_config(net->ipv6.mc_autojoin_sk,
+                              false, pfx, ifindex);
        }
 
        return PTR_ERR(ifp);
@@ -2562,7 +2658,7 @@ static int inet6_addr_del(struct net *net, int ifindex, u32 ifa_flags,
                return -ENODEV;
 
        idev = __in6_dev_get(dev);
-       if (idev == NULL)
+       if (!idev)
                return -ENXIO;
 
        read_lock_bh(&idev->lock);
@@ -2578,6 +2674,10 @@ static int inet6_addr_del(struct net *net, int ifindex, u32 ifa_flags,
                                                 jiffies);
                        ipv6_del_addr(ifp);
                        addrconf_verify_rtnl();
+                       if (ipv6_addr_is_multicast(pfx)) {
+                               ipv6_mc_config(net->ipv6.mc_autojoin_sk,
+                                              false, pfx, dev->ifindex);
+                       }
                        return 0;
                }
        }
@@ -2710,7 +2810,7 @@ static void init_loopback(struct net_device *dev)
        ASSERT_RTNL();
 
        idev = ipv6_find_idev(dev);
-       if (idev == NULL) {
+       if (!idev) {
                pr_debug("%s: add_dev failed\n", __func__);
                return;
        }
@@ -2757,10 +2857,11 @@ static void init_loopback(struct net_device *dev)
        }
 }
 
-static void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_addr *addr)
+static void addrconf_add_linklocal(struct inet6_dev *idev,
+                                  const struct in6_addr *addr, u32 flags)
 {
        struct inet6_ifaddr *ifp;
-       u32 addr_flags = IFA_F_PERMANENT;
+       u32 addr_flags = flags | IFA_F_PERMANENT;
 
 #ifdef CONFIG_IPV6_OPTIMISTIC_DAD
        if (idev->cnf.optimistic_dad &&
@@ -2768,7 +2869,6 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_addr
                addr_flags |= IFA_F_OPTIMISTIC;
 #endif
 
-
        ifp = ipv6_add_addr(idev, addr, NULL, 64, IFA_LINK, addr_flags,
                            INFINITY_LIFE_TIME, INFINITY_LIFE_TIME);
        if (!IS_ERR(ifp)) {
@@ -2778,18 +2878,103 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_addr
        }
 }
 
+static bool ipv6_reserved_interfaceid(struct in6_addr address)
+{
+       if ((address.s6_addr32[2] | address.s6_addr32[3]) == 0)
+               return true;
+
+       if (address.s6_addr32[2] == htonl(0x02005eff) &&
+           ((address.s6_addr32[3] & htonl(0xfe000000)) == htonl(0xfe000000)))
+               return true;
+
+       if (address.s6_addr32[2] == htonl(0xfdffffff) &&
+           ((address.s6_addr32[3] & htonl(0xffffff80)) == htonl(0xffffff80)))
+               return true;
+
+       return false;
+}
+
+static int ipv6_generate_stable_address(struct in6_addr *address,
+                                       u8 dad_count,
+                                       const struct inet6_dev *idev)
+{
+       static DEFINE_SPINLOCK(lock);
+       static __u32 digest[SHA_DIGEST_WORDS];
+       static __u32 workspace[SHA_WORKSPACE_WORDS];
+
+       static union {
+               char __data[SHA_MESSAGE_BYTES];
+               struct {
+                       struct in6_addr secret;
+                       __be32 prefix[2];
+                       unsigned char hwaddr[MAX_ADDR_LEN];
+                       u8 dad_count;
+               } __packed;
+       } data;
+
+       struct in6_addr secret;
+       struct in6_addr temp;
+       struct net *net = dev_net(idev->dev);
+
+       BUILD_BUG_ON(sizeof(data.__data) != sizeof(data));
+
+       if (idev->cnf.stable_secret.initialized)
+               secret = idev->cnf.stable_secret.secret;
+       else if (net->ipv6.devconf_dflt->stable_secret.initialized)
+               secret = net->ipv6.devconf_dflt->stable_secret.secret;
+       else
+               return -1;
+
+retry:
+       spin_lock_bh(&lock);
+
+       sha_init(digest);
+       memset(&data, 0, sizeof(data));
+       memset(workspace, 0, sizeof(workspace));
+       memcpy(data.hwaddr, idev->dev->perm_addr, idev->dev->addr_len);
+       data.prefix[0] = address->s6_addr32[0];
+       data.prefix[1] = address->s6_addr32[1];
+       data.secret = secret;
+       data.dad_count = dad_count;
+
+       sha_transform(digest, data.__data, workspace);
+
+       temp = *address;
+       temp.s6_addr32[2] = (__force __be32)digest[0];
+       temp.s6_addr32[3] = (__force __be32)digest[1];
+
+       spin_unlock_bh(&lock);
+
+       if (ipv6_reserved_interfaceid(temp)) {
+               dad_count++;
+               if (dad_count > dev_net(idev->dev)->ipv6.sysctl.idgen_retries)
+                       return -1;
+               goto retry;
+       }
+
+       *address = temp;
+       return 0;
+}
+
 static void addrconf_addr_gen(struct inet6_dev *idev, bool prefix_route)
 {
-       if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) {
-               struct in6_addr addr;
+       struct in6_addr addr;
+
+       ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0);
 
-               ipv6_addr_set(&addr,  htonl(0xFE800000), 0, 0, 0);
+       if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY) {
+               if (!ipv6_generate_stable_address(&addr, 0, idev))
+                       addrconf_add_linklocal(idev, &addr,
+                                              IFA_F_STABLE_PRIVACY);
+               else if (prefix_route)
+                       addrconf_prefix_route(&addr, 64, idev->dev, 0, 0);
+       } else if (idev->addr_gen_mode == IN6_ADDR_GEN_MODE_EUI64) {
                /* addrconf_add_linklocal also adds a prefix_route and we
                 * only need to care about prefix routes if ipv6_generate_eui64
                 * couldn't generate one.
                 */
                if (ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) == 0)
-                       addrconf_add_linklocal(idev, &addr);
+                       addrconf_add_linklocal(idev, &addr, 0);
                else if (prefix_route)
                        addrconf_prefix_route(&addr, 64, idev->dev, 0, 0);
        }
@@ -2834,7 +3019,7 @@ static void addrconf_sit_config(struct net_device *dev)
         */
 
        idev = ipv6_find_idev(dev);
-       if (idev == NULL) {
+       if (!idev) {
                pr_debug("%s: add_dev failed\n", __func__);
                return;
        }
@@ -2859,7 +3044,7 @@ static void addrconf_gre_config(struct net_device *dev)
        ASSERT_RTNL();
 
        idev = ipv6_find_idev(dev);
-       if (idev == NULL) {
+       if (!idev) {
                pr_debug("%s: add_dev failed\n", __func__);
                return;
        }
@@ -3056,7 +3241,7 @@ static int addrconf_ifdown(struct net_device *dev, int how)
        neigh_ifdown(&nd_tbl, dev);
 
        idev = __in6_dev_get(dev);
-       if (idev == NULL)
+       if (!idev)
                return -ENODEV;
 
        /*
@@ -3127,10 +3312,10 @@ restart:
 
                write_unlock_bh(&idev->lock);
 
-               spin_lock_bh(&ifa->state_lock);
+               spin_lock_bh(&ifa->lock);
                state = ifa->state;
                ifa->state = INET6_IFADDR_STATE_DEAD;
-               spin_unlock_bh(&ifa->state_lock);
+               spin_unlock_bh(&ifa->lock);
 
                if (state != INET6_IFADDR_STATE_DEAD) {
                        __ipv6_ifa_notify(RTM_DELADDR, ifa);
@@ -3288,12 +3473,12 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp)
 {
        bool begin_dad = false;
 
-       spin_lock_bh(&ifp->state_lock);
+       spin_lock_bh(&ifp->lock);
        if (ifp->state != INET6_IFADDR_STATE_DEAD) {
                ifp->state = INET6_IFADDR_STATE_PREDAD;
                begin_dad = true;
        }
-       spin_unlock_bh(&ifp->state_lock);
+       spin_unlock_bh(&ifp->lock);
 
        if (begin_dad)
                addrconf_mod_dad_work(ifp, 0);
@@ -3315,7 +3500,7 @@ static void addrconf_dad_work(struct work_struct *w)
 
        rtnl_lock();
 
-       spin_lock_bh(&ifp->state_lock);
+       spin_lock_bh(&ifp->lock);
        if (ifp->state == INET6_IFADDR_STATE_PREDAD) {
                action = DAD_BEGIN;
                ifp->state = INET6_IFADDR_STATE_DAD;
@@ -3323,7 +3508,7 @@ static void addrconf_dad_work(struct work_struct *w)
                action = DAD_ABORT;
                ifp->state = INET6_IFADDR_STATE_POSTDAD;
        }
-       spin_unlock_bh(&ifp->state_lock);
+       spin_unlock_bh(&ifp->lock);
 
        if (action == DAD_BEGIN) {
                addrconf_dad_begin(ifp);
@@ -3811,7 +3996,7 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
 
        ifm = nlmsg_data(nlh);
        pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx);
-       if (pfx == NULL)
+       if (!pfx)
                return -EINVAL;
 
        ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifm->ifa_flags;
@@ -3923,7 +4108,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
 
        ifm = nlmsg_data(nlh);
        pfx = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer_pfx);
-       if (pfx == NULL)
+       if (!pfx)
                return -EINVAL;
 
        if (tb[IFA_CACHEINFO]) {
@@ -3938,17 +4123,17 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
        }
 
        dev =  __dev_get_by_index(net, ifm->ifa_index);
-       if (dev == NULL)
+       if (!dev)
                return -ENODEV;
 
        ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifm->ifa_flags;
 
        /* We ignore other flags so far. */
        ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR |
-                    IFA_F_NOPREFIXROUTE;
+                    IFA_F_NOPREFIXROUTE | IFA_F_MCAUTOJOIN;
 
        ifa = ipv6_get_ifaddr(net, pfx, dev, 1);
-       if (ifa == NULL) {
+       if (!ifa) {
                /*
                 * It would be best to check for !NLM_F_CREATE here but
                 * userspace already relies on not having to provide this.
@@ -4023,7 +4208,7 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
        u32 preferred, valid;
 
        nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct ifaddrmsg), flags);
-       if (nlh == NULL)
+       if (!nlh)
                return -EMSGSIZE;
 
        put_ifaddrmsg(nlh, ifa->prefix_len, ifa->flags, rt_scope(ifa->scope),
@@ -4052,11 +4237,11 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
        }
 
        if (!ipv6_addr_any(&ifa->peer_addr)) {
-               if (nla_put(skb, IFA_LOCAL, 16, &ifa->addr) < 0 ||
-                   nla_put(skb, IFA_ADDRESS, 16, &ifa->peer_addr) < 0)
+               if (nla_put_in6_addr(skb, IFA_LOCAL, &ifa->addr) < 0 ||
+                   nla_put_in6_addr(skb, IFA_ADDRESS, &ifa->peer_addr) < 0)
                        goto error;
        } else
-               if (nla_put(skb, IFA_ADDRESS, 16, &ifa->addr) < 0)
+               if (nla_put_in6_addr(skb, IFA_ADDRESS, &ifa->addr) < 0)
                        goto error;
 
        if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0)
@@ -4084,11 +4269,11 @@ static int inet6_fill_ifmcaddr(struct sk_buff *skb, struct ifmcaddr6 *ifmca,
                scope = RT_SCOPE_SITE;
 
        nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct ifaddrmsg), flags);
-       if (nlh == NULL)
+       if (!nlh)
                return -EMSGSIZE;
 
        put_ifaddrmsg(nlh, 128, IFA_F_PERMANENT, scope, ifindex);
-       if (nla_put(skb, IFA_MULTICAST, 16, &ifmca->mca_addr) < 0 ||
+       if (nla_put_in6_addr(skb, IFA_MULTICAST, &ifmca->mca_addr) < 0 ||
            put_cacheinfo(skb, ifmca->mca_cstamp, ifmca->mca_tstamp,
                          INFINITY_LIFE_TIME, INFINITY_LIFE_TIME) < 0) {
                nlmsg_cancel(skb, nlh);
@@ -4110,11 +4295,11 @@ static int inet6_fill_ifacaddr(struct sk_buff *skb, struct ifacaddr6 *ifaca,
                scope = RT_SCOPE_SITE;
 
        nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct ifaddrmsg), flags);
-       if (nlh == NULL)
+       if (!nlh)
                return -EMSGSIZE;
 
        put_ifaddrmsg(nlh, 128, IFA_F_PERMANENT, scope, ifindex);
-       if (nla_put(skb, IFA_ANYCAST, 16, &ifaca->aca_addr) < 0 ||
+       if (nla_put_in6_addr(skb, IFA_ANYCAST, &ifaca->aca_addr) < 0 ||
            put_cacheinfo(skb, ifaca->aca_cstamp, ifaca->aca_tstamp,
                          INFINITY_LIFE_TIME, INFINITY_LIFE_TIME) < 0) {
                nlmsg_cancel(skb, nlh);
@@ -4283,7 +4468,7 @@ static int inet6_rtm_getaddr(struct sk_buff *in_skb, struct nlmsghdr *nlh)
                goto errout;
 
        addr = extract_addr(tb[IFA_ADDRESS], tb[IFA_LOCAL], &peer);
-       if (addr == NULL) {
+       if (!addr) {
                err = -EINVAL;
                goto errout;
        }
@@ -4326,7 +4511,7 @@ static void inet6_ifa_notify(int event, struct inet6_ifaddr *ifa)
        int err = -ENOBUFS;
 
        skb = nlmsg_new(inet6_ifaddr_msgsize(), GFP_ATOMIC);
-       if (skb == NULL)
+       if (!skb)
                goto errout;
 
        err = inet6_fill_ifaddr(skb, ifa, 0, 0, event, 0);
@@ -4398,6 +4583,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
        array[DEVCONF_SUPPRESS_FRAG_NDISC] = cnf->suppress_frag_ndisc;
        array[DEVCONF_ACCEPT_RA_FROM_LOCAL] = cnf->accept_ra_from_local;
        array[DEVCONF_ACCEPT_RA_MTU] = cnf->accept_ra_mtu;
+       /* we omit DEVCONF_STABLE_SECRET for now */
 }
 
 static inline size_t inet6_ifla6_size(void)
@@ -4478,24 +4664,24 @@ static int inet6_fill_ifla6_attrs(struct sk_buff *skb, struct inet6_dev *idev)
        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));
-       if (nla == NULL)
+       if (!nla)
                goto nla_put_failure;
        ipv6_store_devconf(&idev->cnf, nla_data(nla), nla_len(nla));
 
        /* XXX - MC not implemented */
 
        nla = nla_reserve(skb, IFLA_INET6_STATS, IPSTATS_MIB_MAX * sizeof(u64));
-       if (nla == NULL)
+       if (!nla)
                goto nla_put_failure;
        snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_STATS, nla_len(nla));
 
        nla = nla_reserve(skb, IFLA_INET6_ICMP6STATS, ICMP6_MIB_MAX * sizeof(u64));
-       if (nla == NULL)
+       if (!nla)
                goto nla_put_failure;
        snmp6_fill_stats(nla_data(nla), idev, IFLA_INET6_ICMP6STATS, nla_len(nla));
 
        nla = nla_reserve(skb, IFLA_INET6_TOKEN, sizeof(struct in6_addr));
-       if (nla == NULL)
+       if (!nla)
                goto nla_put_failure;
 
        if (nla_put_u8(skb, IFLA_INET6_ADDR_GEN_MODE, idev->addr_gen_mode))
@@ -4541,7 +4727,7 @@ static int inet6_set_iftoken(struct inet6_dev *idev, struct in6_addr *token)
 
        ASSERT_RTNL();
 
-       if (token == NULL)
+       if (!token)
                return -EINVAL;
        if (ipv6_addr_any(token))
                return -EINVAL;
@@ -4632,8 +4818,15 @@ static int inet6_set_link_af(struct net_device *dev, const struct nlattr *nla)
                u8 mode = nla_get_u8(tb[IFLA_INET6_ADDR_GEN_MODE]);
 
                if (mode != IN6_ADDR_GEN_MODE_EUI64 &&
-                   mode != IN6_ADDR_GEN_MODE_NONE)
+                   mode != IN6_ADDR_GEN_MODE_NONE &&
+                   mode != IN6_ADDR_GEN_MODE_STABLE_PRIVACY)
                        return -EINVAL;
+
+               if (mode == IN6_ADDR_GEN_MODE_STABLE_PRIVACY &&
+                   !idev->cnf.stable_secret.initialized &&
+                   !dev_net(dev)->ipv6.devconf_dflt->stable_secret.initialized)
+                       return -EINVAL;
+
                idev->addr_gen_mode = mode;
                err = 0;
        }
@@ -4650,7 +4843,7 @@ static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
        void *protoinfo;
 
        nlh = nlmsg_put(skb, portid, seq, event, sizeof(*hdr), flags);
-       if (nlh == NULL)
+       if (!nlh)
                return -EMSGSIZE;
 
        hdr = nlmsg_data(nlh);
@@ -4665,11 +4858,11 @@ static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
            (dev->addr_len &&
             nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) ||
            nla_put_u32(skb, IFLA_MTU, dev->mtu) ||
-           (dev->ifindex != dev->iflink &&
-            nla_put_u32(skb, IFLA_LINK, dev->iflink)))
+           (dev->ifindex != dev_get_iflink(dev) &&
+            nla_put_u32(skb, IFLA_LINK, dev_get_iflink(dev))))
                goto nla_put_failure;
        protoinfo = nla_nest_start(skb, IFLA_PROTINFO);
-       if (protoinfo == NULL)
+       if (!protoinfo)
                goto nla_put_failure;
 
        if (inet6_fill_ifla6_attrs(skb, idev) < 0)
@@ -4730,7 +4923,7 @@ void inet6_ifinfo_notify(int event, struct inet6_dev *idev)
        int err = -ENOBUFS;
 
        skb = nlmsg_new(inet6_if_nlmsg_size(), GFP_ATOMIC);
-       if (skb == NULL)
+       if (!skb)
                goto errout;
 
        err = inet6_fill_ifinfo(skb, idev, 0, 0, event, 0);
@@ -4763,7 +4956,7 @@ static int inet6_fill_prefix(struct sk_buff *skb, struct inet6_dev *idev,
        struct prefix_cacheinfo ci;
 
        nlh = nlmsg_put(skb, portid, seq, event, sizeof(*pmsg), flags);
-       if (nlh == NULL)
+       if (!nlh)
                return -EMSGSIZE;
 
        pmsg = nlmsg_data(nlh);
@@ -4802,7 +4995,7 @@ static void inet6_prefix_notify(int event, struct inet6_dev *idev,
        int err = -ENOBUFS;
 
        skb = nlmsg_new(inet6_prefix_nlmsg_size(), GFP_ATOMIC);
-       if (skb == NULL)
+       if (!skb)
                goto errout;
 
        err = inet6_fill_prefix(skb, idev, pinfo, 0, 0, event, 0);
@@ -5042,6 +5235,74 @@ int addrconf_sysctl_proxy_ndp(struct ctl_table *ctl, int write,
        return ret;
 }
 
+static int addrconf_sysctl_stable_secret(struct ctl_table *ctl, int write,
+                                        void __user *buffer, size_t *lenp,
+                                        loff_t *ppos)
+{
+       int err;
+       struct in6_addr addr;
+       char str[IPV6_MAX_STRLEN];
+       struct ctl_table lctl = *ctl;
+       struct net *net = ctl->extra2;
+       struct ipv6_stable_secret *secret = ctl->data;
+
+       if (&net->ipv6.devconf_all->stable_secret == ctl->data)
+               return -EIO;
+
+       lctl.maxlen = IPV6_MAX_STRLEN;
+       lctl.data = str;
+
+       if (!rtnl_trylock())
+               return restart_syscall();
+
+       if (!write && !secret->initialized) {
+               err = -EIO;
+               goto out;
+       }
+
+       if (!write) {
+               err = snprintf(str, sizeof(str), "%pI6",
+                              &secret->secret);
+               if (err >= sizeof(str)) {
+                       err = -EIO;
+                       goto out;
+               }
+       }
+
+       err = proc_dostring(&lctl, write, buffer, lenp, ppos);
+       if (err || !write)
+               goto out;
+
+       if (in6_pton(str, -1, addr.in6_u.u6_addr8, -1, NULL) != 1) {
+               err = -EIO;
+               goto out;
+       }
+
+       secret->initialized = true;
+       secret->secret = addr;
+
+       if (&net->ipv6.devconf_dflt->stable_secret == ctl->data) {
+               struct net_device *dev;
+
+               for_each_netdev(net, dev) {
+                       struct inet6_dev *idev = __in6_dev_get(dev);
+
+                       if (idev) {
+                               idev->addr_gen_mode =
+                                       IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
+                       }
+               }
+       } else {
+               struct inet6_dev *idev = ctl->extra1;
+
+               idev->addr_gen_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY;
+       }
+
+out:
+       rtnl_unlock();
+
+       return err;
+}
 
 static struct addrconf_sysctl_table
 {
@@ -5314,6 +5575,13 @@ static struct addrconf_sysctl_table
                        .mode           = 0644,
                        .proc_handler   = proc_dointvec,
                },
+               {
+                       .procname       = "stable_secret",
+                       .data           = &ipv6_devconf.stable_secret,
+                       .maxlen         = IPV6_MAX_STRLEN,
+                       .mode           = 0600,
+                       .proc_handler   = addrconf_sysctl_stable_secret,
+               },
                {
                        /* sentinel */
                }
@@ -5328,7 +5596,7 @@ static int __addrconf_sysctl_register(struct net *net, char *dev_name,
        char path[sizeof("net/ipv6/conf/") + IFNAMSIZ];
 
        t = kmemdup(&addrconf_sysctl, sizeof(*t), GFP_KERNEL);
-       if (t == NULL)
+       if (!t)
                goto out;
 
        for (i = 0; t->addrconf_vars[i].data; i++) {
@@ -5340,7 +5608,7 @@ static int __addrconf_sysctl_register(struct net *net, char *dev_name,
        snprintf(path, sizeof(path), "net/ipv6/conf/%s", dev_name);
 
        t->sysctl_header = register_net_sysctl(net, path, t->addrconf_vars);
-       if (t->sysctl_header == NULL)
+       if (!t->sysctl_header)
                goto free;
 
        p->sysctl = t;
@@ -5356,7 +5624,7 @@ static void __addrconf_sysctl_unregister(struct ipv6_devconf *p)
 {
        struct addrconf_sysctl_table *t;
 
-       if (p->sysctl == NULL)
+       if (!p->sysctl)
                return;
 
        t = p->sysctl;
@@ -5399,17 +5667,20 @@ static int __net_init addrconf_init_net(struct net *net)
        struct ipv6_devconf *all, *dflt;
 
        all = kmemdup(&ipv6_devconf, sizeof(ipv6_devconf), GFP_KERNEL);
-       if (all == NULL)
+       if (!all)
                goto err_alloc_all;
 
        dflt = kmemdup(&ipv6_devconf_dflt, sizeof(ipv6_devconf_dflt), GFP_KERNEL);
-       if (dflt == NULL)
+       if (!dflt)
                goto err_alloc_dflt;
 
        /* these will be inherited by all namespaces */
        dflt->autoconf = ipv6_defaults.autoconf;
        dflt->disable_ipv6 = ipv6_defaults.disable_ipv6;
 
+       dflt->stable_secret.initialized = false;
+       all->stable_secret.initialized = false;
+
        net->ipv6.devconf_all = all;
        net->ipv6.devconf_dflt = dflt;