Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5f40ef77 authored by Hannes Frederic Sowa's avatar Hannes Frederic Sowa Committed by David S. Miller
Browse files

ipv6: do retries on stable privacy addresses



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: default avatarHannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8e8e676d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ struct inet6_ifaddr {

	__u32			flags;
	__u8			dad_probes;
	__u8			stable_privacy_retry;

	__u16			scope;

+54 −3
Original line number Diff line number Diff line
@@ -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);