netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: David Miller <davem@davemloft.net>
To: netdev@vger.kernel.org
Cc: nhorman@tuxdriver.com
Subject: The mystery of optimistic ipv6 DAD handling
Date: Tue, 27 Dec 2011 14:06:52 -0500 (EST)	[thread overview]
Message-ID: <20111227.140652.1729386263761283235.davem@davemloft.net> (raw)


This afternoon I audited the neigh attachment logic for ipv6 routes
and found the following gem.

Since the beginning the optimistic DAD support has this code in
ipv6_add_addr():

	/*
	 * part one of RFC 4429, section 3.3
	 * We should not configure an address as
	 * optimistic if we do not yet know the link
	 * layer address of our nexhop router
	 */

	if (dst_get_neighbour_noref_raw(&rt->dst) == NULL)
		ifa->flags &= ~IFA_F_OPTIMISTIC;

(back at inclusion time the test was "rt->rt6i_nexthop == NULL" which
 is the same, we're just hiding it behind a function now so we can
 change the implementation)

But this is never, ever, true.

addrconf_dst_alloc() will never give you a route with a NULL neighbour.

If the neigh lookup fails, it puts the route and returns a pointer error:

	neigh = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway, rt->rt6i_dev);
	if (IS_ERR(neigh)) {
		dst_free(&rt->dst);

		return ERR_CAST(neigh);
	}
	dst_set_neighbour(&rt->dst, neigh);

I verified that back in 2.6.21 when the optimistic DAD code went in,
this same logic was present:

	rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway);
	if (rt->rt6i_nexthop == NULL) {
		dst_free(&rt->u.dst);
		return ERR_PTR(-ENOMEM);
	}

But there's a catch, ndisc_get_neigh() has changed over the years, it used to:

static inline struct neighbour * ndisc_get_neigh(struct net_device *dev, struct in6_addr *addr)                                   
{                                                                                                                                 
                                                                                                                                  
        if (dev)                                                                                                                  
                return __neigh_lookup(&nd_tbl, addr, dev, 1);                                                                     
                                                                                                                                  
        return NULL;                                                                                                              
}                                                                                                                                 

but then it was changed to say:

--------------------
 	if (dev)
-		return __neigh_lookup(&nd_tbl, addr, dev, 1);
+		return __neigh_lookup_errno(&nd_tbl, addr, dev);
 
-	return NULL;
+	return ERR_PTR(-ENODEV);
--------------------

in order to fix a DoS type scenerio where the neighbour cache could quickly fill up,
this is commit:

--------------------
commit 14deae41566b5cdd992c01d0069518ced5227c83
Author: David S. Miller <davem@davemloft.net>
Date:   Sun Jan 4 16:04:39 2009 -0800

    ipv6: Fix sporadic sendmsg -EINVAL when sending to multicast groups.
--------------------

Obviously, this could change the optimistic DaD logic.  However, two things strike me:

1) even beforehand we'd only get a NULL neigh when the device pointer
   is NULL, that can't happen here, rt->rt6i_dev will be non-NULL always
   in these circumstances, it's forced to be net->loopback_dev by the
   ip6_dst_alloc() call made by addrconf_dst_alloc() (the code back
   in 2.6.21 does the same exact thing, just inline)

2) on top of that it's looking up a neighbour using rt->rt6i_gateway
   as the key, that's either bogus or pointless.  As far as I can tell
   it's uninitialized at this point and therefore always all-zeros.

My quick hunch is that the neigh should be looked up based upon 'addr', and
that ipv6_add_addr() should test if the neigh is resolved in order to determine
if the optimistic flag should be cleared.  That matches the logic in the comment
"do not yet know the link layer address of our nexthop router."

Neil, any chance you can help me unravel this?

Thanks.

             reply	other threads:[~2011-12-27 19:08 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-12-27 19:06 David Miller [this message]
2011-12-27 19:53 ` The mystery of optimistic ipv6 DAD handling David Miller
2011-12-28 15:19   ` Neil Horman
2011-12-28 18:39     ` David Miller
2011-12-28 19:21       ` Neil Horman
2011-12-28 19:38         ` David Miller
2011-12-29  0:00           ` Neil Horman
2012-01-03 19:45           ` Neil Horman
2012-01-03 19:48             ` David Miller

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=20111227.140652.1729386263761283235.davem@davemloft.net \
    --to=davem@davemloft.net \
    --cc=netdev@vger.kernel.org \
    --cc=nhorman@tuxdriver.com \
    /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).