ipv6: do retries on stable privacy addresses
authorHannes Frederic Sowa <hannes@stressinduktion.org>
Mon, 23 Mar 2015 22:36:04 +0000 (23:36 +0100)
committerDavid S. Miller <davem@davemloft.net>
Tue, 24 Mar 2015 02:12:09 +0000 (22:12 -0400)
If a DAD conflict is detected, we want to retry privacy stable address
generation up to idgen_retries (= 3) times with a delay of idgen_delay
(= 1 second). Add the logic to addrconf_dad_failure.

By design, we don't clean up dad failed permanent addresses.

Cc: Erik Kline <ek@google.com>
Cc: Fernando Gont <fgont@si6networks.com>
Cc: Lorenzo Colitti <lorenzo@google.com>
Cc: YOSHIFUJI Hideaki/吉藤英明 <hideaki.yoshifuji@miraclelinux.com>
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/if_inet6.h
net/ipv6/addrconf.c

index d89397a229036d77b57343a69ed14072fb02373a..1c8b6820b69476f7247baac12183a6f5f5fe2275 100644 (file)
@@ -52,6 +52,7 @@ struct inet6_ifaddr {
 
        __u32                   flags;
        __u8                    dad_probes;
+       __u8                    stable_privacy_retry;
 
        __u16                   scope;
 
index 1cc5320e510f241c532661d208679184ae1b8262..9b51fdb42ba9c99d24174949d548c1b91a564857 100644 (file)
@@ -1710,6 +1710,7 @@ static int addrconf_dad_end(struct inet6_ifaddr *ifp)
 
 void addrconf_dad_failure(struct inet6_ifaddr *ifp)
 {
+       struct in6_addr addr;
        struct inet6_dev *idev = ifp->idev;
 
        if (addrconf_dad_end(ifp)) {
@@ -1720,9 +1721,59 @@ 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;
+               const unsigned int idgen_retries = 3;
+               const unsigned int idgen_delay = 1 * HZ;
+               int retries = ifp->stable_privacy_retry + 1;
+
+               if (retries > 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, 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;
 
@@ -1736,7 +1787,7 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp)
                }
        }
 
-       spin_lock_bh(&ifp->lock);
+errdad:
        /* transition from _POSTDAD to _ERRDAD */
        ifp->state = INET6_IFADDR_STATE_ERRDAD;
        spin_unlock_bh(&ifp->lock);