netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Toyo Abe <tabe@miraclelinux.com>
To: "YOSHIFUJI Hideaki / 吉藤英明" <yoshfuji@linux-ipv6.org>,
	davem@davemloft.net
Cc: netdev@vger.kernel.org, Toyo Abe <tabe@miraclelinux.com>
Subject: [IPV6] ADDRCONF: Defer dad for global address until dad for linklocal is completed.
Date: Tue, 20 May 2008 12:37:54 +0900	[thread overview]
Message-ID: <48324792.5070309@miraclelinux.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 1444 bytes --]

When RA packet with prefix option is received during processing DAD for linklocal
address, Linux initiates DAD for global address derived from the received prefix.
It can be succeeded even if a duplicated linklocal address is detected.

RFC4862 5.4.5, which describes the behaviour on DAD failure, says;

   If the address is a link-local address formed from an interface
   identifier based on the hardware address, which is supposed to be
   uniquely assigned (e.g., EUI-64 for an Ethernet interface), IP
   operation on the interface SHOULD be disabled.  By disabling IP
   operation, the node will then:

   -  not send any IP packets from the interface,

   -  silently drop any IP packets received on the interface, and

   -  not forward any IP packets to the interface (when acting as a
      router or processing a packet with a Routing header).

This problem was observed by testing with beta version of TAHI test suite (v4.0.0b2)
- Stateless Address Autoconfiguration test #3, #5, #14, and #15 force dad for linklocal
to be failed and send RA to the host, then check if the host doesn't respond to
DAD NS with respect to its global address. However, 2.6.26-rc2 send DAD NA in response
to the DAD NS so the test scenarios were failed.

This patch fixes the problem by deferring DAD initiation for global address until
DAD for linklocal address is completed. Now the failed test scenarios noted above
are all passed.

Thank you,
-toyo

[-- Attachment #2: IPV6-ADDRCONF-Defer-dad-for-global-address-until-dad-for-linklocal-is-completed.patch --]
[-- Type: text/x-patch, Size: 5572 bytes --]

[IPV6] ADDRCONF: Defer dad for global address until dad for linklocal is completed.

When RA packet with prefix option is received during processing DAD for linklocal
address, Linux initiates DAD for global address derived from the received prefix.
It can be succeeded even if a duplicated linklocal address is detected.

RFC4862 5.4.5, which describes the behaviour on DAD failure, says;

   If the address is a link-local address formed from an interface
   identifier based on the hardware address, which is supposed to be
   uniquely assigned (e.g., EUI-64 for an Ethernet interface), IP
   operation on the interface SHOULD be disabled.  By disabling IP
   operation, the node will then:

   -  not send any IP packets from the interface,

   -  silently drop any IP packets received on the interface, and

   -  not forward any IP packets to the interface (when acting as a
      router or processing a packet with a Routing header).

This problem was observed by testing with beta version of TAHI test suite (v4.0.0b2)
- Stateless Address Autoconfiguration test #3, #5, #14, and #15 force dad for linklocal
to be failed and send RA to the host, then check if the host doesn't respond to
DAD NS with respect to its global address. However, 2.6.26-rc2 send DAD NA in response
to the DAD NS so the test scenarios were failed.

This patch fixes the problem by deferring DAD initiation for global address until
DAD for linklocal address is completed. Now the failed test scenarios noted above
are all passed.

Signed-off-by: Toyo Abe <tabe@miraclelinux.com>

---
 include/linux/if_addr.h |    1 +
 net/ipv6/addrconf.c     |   62 ++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 54 insertions(+), 9 deletions(-)

diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
index 43f3bed..75ff0b8 100644
--- a/include/linux/if_addr.h
+++ b/include/linux/if_addr.h
@@ -40,6 +40,7 @@ enum
 
 #define	IFA_F_NODAD		0x02
 #define IFA_F_OPTIMISTIC	0x04
+#define IFA_F_PENDDAD		0x08
 #define	IFA_F_HOMEADDRESS	0x10
 #define IFA_F_DEPRECATED	0x20
 #define IFA_F_TENTATIVE		0x40
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index e591e09..3080ded 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1532,21 +1532,20 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
 	return -1;
 }
 
-static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev)
+static struct inet6_ifaddr *ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev)
 {
-	int err = -1;
 	struct inet6_ifaddr *ifp;
 
 	read_lock_bh(&idev->lock);
 	for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
-		if (ifp->scope == IFA_LINK && !(ifp->flags&IFA_F_TENTATIVE)) {
+		if (ifp->scope == IFA_LINK) {
+			in6_ifa_hold(ifp);
 			memcpy(eui, ifp->addr.s6_addr+8, 8);
-			err = 0;
 			break;
 		}
 	}
 	read_unlock_bh(&idev->lock);
-	return err;
+	return ifp;
 }
 
 #ifdef CONFIG_IPV6_PRIVACY
@@ -1808,13 +1807,13 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len)
 
 	if (pinfo->autoconf && in6_dev->cnf.autoconf) {
 		struct inet6_ifaddr * ifp;
+		struct inet6_ifaddr * ifl;
 		struct in6_addr addr;
 		int create = 0, update_lft = 0;
 
 		if (pinfo->prefix_len == 64) {
 			memcpy(&addr, &pinfo->prefix, 8);
-			if (ipv6_generate_eui64(addr.s6_addr + 8, dev) &&
-			    ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) {
+			if ((ifl = ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev)) == NULL) {
 				in6_dev_put(in6_dev);
 				return;
 			}
@@ -1832,7 +1831,9 @@ ok:
 
 		if (ifp == NULL && valid_lft) {
 			int max_addresses = in6_dev->cnf.max_addresses;
-			u32 addr_flags = 0;
+			u32 addr_flags = IFA_F_PENDDAD;
+			struct inet6_dev *idev;
+			int dad_now = 0;
 
 #ifdef CONFIG_IPV6_OPTIMISTIC_DAD
 			if (in6_dev->cnf.optimistic_dad &&
@@ -1856,7 +1857,28 @@ ok:
 
 			update_lft = create = 1;
 			ifp->cstamp = jiffies;
-			addrconf_dad_start(ifp, RTF_ADDRCONF|RTF_PREFIX_RT);
+
+			/*
+			 * Defer dad for global address on the idev until
+			 * dad for linklocal is completed.
+			 */
+			idev = ifp->idev;
+			read_lock_bh(&idev->lock);
+			if (!(ifl->flags & IFA_F_TENTATIVE)) {
+				spin_lock(&ifp->lock);
+			    	if (ifp->flags & IFA_F_PENDDAD) {
+					ifp->flags &= ~IFA_F_PENDDAD;
+					dad_now = 1;
+				}
+				spin_unlock(&ifp->lock);
+			}
+			read_unlock_bh(&idev->lock);
+			/* Since we got a ifl->refcnt in ipv6_inherit_eui64(), release it. */
+			in6_ifa_put(ifl);
+
+			/* If dad for linklocal is already completed, start dad right away. */
+			if (dad_now)
+				addrconf_dad_start(ifp, RTF_ADDRCONF|RTF_PREFIX_RT);
 		}
 
 		if (ifp) {
@@ -2780,6 +2802,23 @@ out:
 	in6_ifa_put(ifp);
 }
 
+static void addrconf_dad_kick_pending(struct inet6_dev *idev) {
+	struct inet6_ifaddr *ifp;
+
+	read_lock_bh(&idev->lock);
+	for (ifp = idev->addr_list; ifp; ifp = ifp->if_next) {
+		spin_lock_bh(&ifp->lock);
+		if (!(ifp->flags & (IFA_F_TENTATIVE|IFA_F_PENDDAD))) {
+			spin_unlock_bh(&ifp->lock);
+			continue;
+		}
+		ifp->flags &= ~IFA_F_PENDDAD;
+		spin_unlock_bh(&ifp->lock);
+		addrconf_dad_kick(ifp);
+	}
+	read_unlock_bh(&idev->lock);
+}
+
 static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
 {
 	struct net_device *	dev = ifp->idev->dev;
@@ -2790,6 +2829,11 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
 
 	ipv6_ifa_notify(RTM_NEWADDR, ifp);
 
+	if ((dev->flags&IFF_LOOPBACK) == 0 &&
+	    (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) {
+		addrconf_dad_kick_pending(ifp->idev);
+	}
+
 	/* If added prefix is link local and forwarding is off,
 	   start sending router solicitations.
 	 */

             reply	other threads:[~2008-05-20  4:04 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-05-20  3:37 Toyo Abe [this message]
2008-05-20  8:33 ` [IPV6] ADDRCONF: Defer dad for global address until dad for linklocal is completed YOSHIFUJI Hideaki / 吉藤英明
2008-05-20 12:27   ` Toyo Abe
2008-05-20 12:35     ` Toyo Abe
2008-05-20 12:53       ` Toyo Abe
2008-05-21 16:58     ` YOSHIFUJI Hideaki / 吉藤英明
2008-05-21 17:30       ` Toyo Abe
2008-05-21 20:30         ` David Miller
2008-06-20  2:17       ` Toyo Abe
2008-06-20  2:32         ` YOSHIFUJI Hideaki / 吉藤英明

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=48324792.5070309@miraclelinux.com \
    --to=tabe@miraclelinux.com \
    --cc=davem@davemloft.net \
    --cc=netdev@vger.kernel.org \
    --cc=yoshfuji@linux-ipv6.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).