All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
@ 2007-01-19 21:23 Neil Horman
  2007-01-19 23:05 ` YOSHIFUJI Hideaki / 吉藤英明
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-01-19 21:23 UTC (permalink / raw)
  To: davem, kuznet, pekkas, jmorris, yoshfuji, kaber; +Cc: netdev, nhorman

Patch to Implement IPv6 RFC 4429 (Optimistic Duplicate Address Detection).  In
short, this is a feature whereby a node with a Tentative address can begin to
make use of that address almost immediately after its configured.  To enable
this, extra rules need to be followed during the Duplicate address detection
phase of the addresses configuration, so that in the event of a collision,
neighboring nodes do not have thier neighbor caches affected adversely by the
optimistic node.  This patch implements those rules as outlined in the RFC.

I have a fairly limited testing environment here, but from the testing I've
done, this patch appears to conform to the rules as outlined in RFC 4429, causes
no adverse affects on normal IPv6 operation when in use, and doesn't seem to
break anything when disabled via the sysctl.

Comments and Reviews appreciated.

Thanks and Regards
Neil


Signed-Off-By: Neil Horman <nhorman@tuxdriver.com>


 include/linux/if_addr.h    |    1 
 include/linux/sysctl.h     |    1 
 include/net/addrconf.h     |    4 ++-
 include/net/ipv6.h         |    1 
 net/ipv6/addrconf.c        |   50 +++++++++++++++++++++++++++++++-------
 net/ipv6/mcast.c           |    4 +--
 net/ipv6/ndisc.c           |   59 ++++++++++++++++++++++++++++++++++++++-------
 net/ipv6/sysctl_net_ipv6.c |    8 ++++++
 8 files changed, 107 insertions(+), 21 deletions(-)


diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
index d557e4c..43f3bed 100644
--- a/include/linux/if_addr.h
+++ b/include/linux/if_addr.h
@@ -39,6 +39,7 @@ enum
 #define IFA_F_TEMPORARY		IFA_F_SECONDARY
 
 #define	IFA_F_NODAD		0x02
+#define IFA_F_OPTIMISTIC	0x04
 #define	IFA_F_HOMEADDRESS	0x10
 #define IFA_F_DEPRECATED	0x20
 #define IFA_F_TENTATIVE		0x40
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 81480e6..62034c3 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -531,6 +531,7 @@ enum {
 	NET_IPV6_IP6FRAG_TIME=23,
 	NET_IPV6_IP6FRAG_SECRET_INTERVAL=24,
 	NET_IPV6_MLD_MAX_MSF=25,
+	NET_IPV6_OPT_DAD_ENABLE=26,
 };
 
 enum {
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 88df8fc..d248a19 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -73,7 +73,9 @@ extern int			ipv6_get_saddr(struct dst_entry *dst,
 extern int			ipv6_dev_get_saddr(struct net_device *dev, 
 					       struct in6_addr *daddr,
 					       struct in6_addr *saddr);
-extern int			ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
+extern int			ipv6_get_lladdr(struct net_device *dev, 
+						struct in6_addr *,
+						unsigned char banned_flags);
 extern int			ipv6_rcv_saddr_equal(const struct sock *sk, 
 						      const struct sock *sk2);
 extern void			addrconf_join_solict(struct net_device *dev,
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index 00328b7..dd16169 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -110,6 +110,7 @@ struct frag_hdr {
 /* sysctls */
 extern int sysctl_ipv6_bindv6only;
 extern int sysctl_mld_max_msf;
+extern int sysctl_optimistic_dad;
 
 /* MIBs */
 DECLARE_SNMP_STAT(struct ipstats_mib, ipv6_statistics);
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 2a7e461..f7afb2a 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -206,6 +206,8 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
 	.proxy_ndp		= 0,
 };
 
+int sysctl_optimistic_dad = 1;
+
 /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
 #if 0
 const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
@@ -830,7 +832,8 @@ retry:
 	ift = !max_addresses ||
 	      ipv6_count_addresses(idev) < max_addresses ? 
 		ipv6_add_addr(idev, &addr, tmp_plen,
-			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
+			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
+				IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
 	if (!ift || IS_ERR(ift)) {
 		in6_ifa_put(ifp);
 		in6_dev_put(idev);
@@ -1174,7 +1177,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
 }
 
 
-int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
+int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, 
+			unsigned char banned_flags)
 {
 	struct inet6_dev *idev;
 	int err = -EADDRNOTAVAIL;
@@ -1185,7 +1189,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
 
 		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 && !(ifp->flags&banned_flags)) {
 				ipv6_addr_copy(addr, &ifp->addr);
 				err = 0;
 				break;
@@ -1742,7 +1746,7 @@ ok:
 			if (!max_addresses ||
 			    ipv6_count_addresses(in6_dev) < max_addresses)
 				ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len,
-						    addr_type&IPV6_ADDR_SCOPE_MASK, 0);
+						    addr_type&IPV6_ADDR_SCOPE_MASK,0);
 
 			if (!ifp || IS_ERR(ifp)) {
 				in6_dev_put(in6_dev);
@@ -1751,6 +1755,7 @@ ok:
 
 			update_lft = create = 1;
 			ifp->cstamp = jiffies;
+			ifp->flags |= IFA_F_OPTIMISTIC;
 			addrconf_dad_start(ifp, RTF_ADDRCONF|RTF_PREFIX_RT);
 		}
 
@@ -1945,7 +1950,11 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
 		ifp->prefered_lft = prefered_lft;
 		ifp->tstamp = jiffies;
 		spin_unlock_bh(&ifp->lock);
-
+		/*
+		 * Note that section 3.1 of RFC 4429 indicates
+		 * That the Optimistic flag should not be set for
+		 * manually configured addresses
+		 */
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
 		addrconf_verify(0);
@@ -2123,7 +2132,8 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr
 {
 	struct inet6_ifaddr * ifp;
 
-	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
+	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, 
+		IFA_F_PERMANENT|IFA_F_OPTIMISTIC);
 	if (!IS_ERR(ifp)) {
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
@@ -2190,7 +2200,7 @@ ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
 {
 	struct in6_addr lladdr;
 
-	if (!ipv6_get_lladdr(link_dev, &lladdr)) {
+	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
 		addrconf_add_linklocal(idev, &lladdr);
 		return 0;
 	}
@@ -2537,8 +2547,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	struct inet6_dev *idev = ifp->idev;
 	struct net_device *dev = idev->dev;
 
+	if (!sysctl_optimistic_dad)
+		ifp->flags &= ~IFA_F_OPTIMISTIC;
+
 	addrconf_join_solict(dev, &ifp->addr);
 
+	/*
+	 * Optimistic nodes need to joing the anycast address
+	 * right away
+	 */
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addrconf_join_anycast(ifp);
+
 	if (ifp->prefix_len != 128 && (ifp->flags&IFA_F_PERMANENT))
 		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 0,
 					flags);
@@ -2553,7 +2573,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
 	    !(ifp->flags&IFA_F_TENTATIVE) ||
 	    ifp->flags & IFA_F_NODAD) {
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -2573,6 +2593,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 		addrconf_dad_stop(ifp);
 		return;
 	}
+
+	/*
+	 * Forwarding devices (routers) should not use
+	 * optimistic addresses
+	 * Nor should interfaces that don't know the 
+	 * Source address for their default gateway
+	 * RFC 4429 Sec 3.3
+	 */
+	if ((ipv6_devconf.forwarding) ||
+	   (ifp->rt == NULL))
+		ifp->flags &= ~IFA_F_OPTIMISTIC;
+
 	addrconf_dad_kick(ifp);
 	spin_unlock_bh(&ifp->lock);
 out:
@@ -2597,7 +2629,7 @@ static void addrconf_dad_timer(unsigned long data)
 		 * DAD was successful
 		 */
 
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 882cde4..9c5273c 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
@@ -1789,7 +1789,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 6a9f616..a5bcba1 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -498,7 +498,21 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
         msg->icmph.icmp6_unused = 0;
         msg->icmph.icmp6_router    = router;
         msg->icmph.icmp6_solicited = solicited;
-        msg->icmph.icmp6_override  = override;
+	if (!ifp || !(ifp->flags & IFA_F_OPTIMISTIC))
+		msg->icmph.icmp6_override  = override;
+	else {
+		/*
+		 * We must clear the override flag on all
+		 * neighbor advertisements from source 
+		 * addresses that are OPTIMISTIC - RFC 4429
+		 * section 2.2
+		 */
+		if (override)
+			printk(KERN_WARNING
+				"Disallowing override flag for OPTIMISTIC addr\n");
+		msg->icmph.icmp6_override = 0;
+	}
+
 
         /* Set the target address. */
 	ipv6_addr_copy(&msg->target, solicited_addr);
@@ -542,7 +556,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
 	int send_llinfo;
 
 	if (saddr == NULL) {
-		if (ipv6_get_lladdr(dev, &addr_buf))
+		if (ipv6_get_lladdr(dev, &addr_buf,
+			(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
 			return;
 		saddr = &addr_buf;
 	}
@@ -622,9 +637,20 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
         struct sk_buff *skb;
         struct icmp6hdr *hdr;
 	__u8 * opt;
+	struct inet6_ifaddr *ifp;
         int len;
 	int err;
 
+	/*
+	 * Check the source address.  If its OPTIMISTIC
+	 * and addr_len is non-zero (implying the sllao option)
+	 * then don't send the RS (RFC 4429, section 2.2)
+	 */
+	ifp = ipv6_get_ifaddr(saddr, dev, 1);
+
+	if ((!ifp) || ((ifp->flags & IFA_F_OPTIMISTIC) && dev->addr_len))
+		return;
+
 	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
 			dev->ifindex);
 
@@ -746,6 +772,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	int dad = ipv6_addr_any(saddr);
 	int inc;
 	int is_router;
+	int type;
 
 	if (ipv6_addr_is_multicast(&msg->target)) {
 		ND_PRINTK2(KERN_WARNING 
@@ -796,11 +823,13 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	inc = ipv6_addr_is_multicast(daddr);
 
 	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
-		if (ifp->flags & IFA_F_TENTATIVE) {
+
+		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
 			/* Address is tentative. If the source
-			   is unspecified address, it is someone
-			   does DAD, otherwise we ignore solicitations
-			   until DAD timer expires.
+			   is unspecified address, someone else
+			   is doing DAD, and if its not us, then
+			   we need to fail our own DAD
+			   RFC 4429 Sec 3.3
 			 */
 			if (!dad)
 				goto out;
@@ -816,8 +845,20 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 					goto out;
 				}
 			}
-			addrconf_dad_failure(ifp); 
-			return;
+
+			/* The one exception to the above rule about 
+			   optimistic addresses is that we need to always 
+			   respond to an NS from a unicast address if we are
+			   optimistic. RFC 4429 Sec 3.3.  If (unicast
+			   and optimistic) are false then we can just fail
+			   dad now.
+			*/
+			type = ipv6_addr_type(saddr);			
+			if (!((ifp->flags & IFA_F_OPTIMISTIC) && 
+			    (type & IPV6_ADDR_UNICAST))) {
+				addrconf_dad_failure(ifp); 
+				return;
+			}
 		}
 
 		idev = ifp->idev;
@@ -1406,7 +1447,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
 
 	dev = skb->dev;
 
-	if (ipv6_get_lladdr(dev, &saddr_buf)) {
+	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
 		ND_PRINTK2(KERN_WARNING
 			   "ICMPv6 Redirect: no link-local address on %s\n",
 			   dev->name);
diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c
index 7a4639d..3a8dee5 100644
--- a/net/ipv6/sysctl_net_ipv6.c
+++ b/net/ipv6/sysctl_net_ipv6.c
@@ -80,6 +80,14 @@ static ctl_table ipv6_table[] = {
 		.mode		= 0644,
 		.proc_handler	= &proc_dointvec
 	},
+	{
+		.ctl_name	= NET_IPV6_OPT_DAD_ENABLE,
+		.procname	= "ip6optimistic_dad_enable",
+		.data		= &sysctl_optimistic_dad,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec
+	},
 	{ .ctl_name = 0 }
 };
 

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-19 21:23 [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection Neil Horman
@ 2007-01-19 23:05 ` YOSHIFUJI Hideaki / 吉藤英明
  2007-01-20  1:41   ` Neil Horman
  2007-01-22 18:15   ` Neil Horman
  0 siblings, 2 replies; 71+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2007-01-19 23:05 UTC (permalink / raw)
  To: nhorman; +Cc: davem, kuznet, pekkas, jmorris, kaber, netdev, yoshfuji

Hello.

In article <20070119212314.GA10748@hmsreliant.homelinux.net> (at Fri, 19 Jan 2007 16:23:14 -0500), Neil Horman <nhorman@tuxdriver.com> says:

> Patch to Implement IPv6 RFC 4429 (Optimistic Duplicate Address Detection).  In

Good work.  We will see if this would break core and basic ipv6 code.
Dave, please hold on.

Some quick comments.

> --- a/include/net/ipv6.h
> +++ b/include/net/ipv6.h
> @@ -110,6 +110,7 @@ struct frag_hdr {
>  /* sysctls */
>  extern int sysctl_ipv6_bindv6only;
>  extern int sysctl_mld_max_msf;
> +extern int sysctl_optimistic_dad;
>  
:
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index 2a7e461..f7afb2a 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -206,6 +206,8 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
>  	.proxy_ndp		= 0,
>  };
>  
> +int sysctl_optimistic_dad = 1;
> +

Please put this into ipv6_devconf{} and make it per-interface variable.
And I think default should be kept off (0).

>  /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
>  #if 0
>  const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
> @@ -830,7 +832,8 @@ retry:
>  	ift = !max_addresses ||
>  	      ipv6_count_addresses(idev) < max_addresses ? 
>  		ipv6_add_addr(idev, &addr, tmp_plen,
> -			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
> +			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
> +				IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
>  	if (!ift || IS_ERR(ift)) {
>  		in6_ifa_put(ifp);
>  		in6_dev_put(idev);

Please align ipv6_addr_type and IFA_F_TEMPORARY

> @@ -1174,7 +1177,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
>  }
>  
>  
> -int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
> +int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, 
> +			unsigned char banned_flags)
>  {
>  	struct inet6_dev *idev;
>  	int err = -EADDRNOTAVAIL;

Please align "struct net_device" and "unsigned char".

> @@ -1185,7 +1189,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
>  
>  		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 && !(ifp->flags&banned_flags)) {
>  				ipv6_addr_copy(addr, &ifp->addr);
>  				err = 0;
>  				break;
> @@ -1742,7 +1746,7 @@ ok:

It is not your fault, but please put a space around "&".

>  			if (!max_addresses ||
>  			    ipv6_count_addresses(in6_dev) < max_addresses)
>  				ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len,
> -						    addr_type&IPV6_ADDR_SCOPE_MASK, 0);
> +						    addr_type&IPV6_ADDR_SCOPE_MASK,0);
>  
>  			if (!ifp || IS_ERR(ifp)) {
>  				in6_dev_put(in6_dev);

Please do no kill space after ",".

> @@ -2123,7 +2132,8 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr
>  {
>  	struct inet6_ifaddr * ifp;
>  
> -	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
> +	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, 
> +		IFA_F_PERMANENT|IFA_F_OPTIMISTIC);
>  	if (!IS_ERR(ifp)) {
>  		addrconf_dad_start(ifp, 0);
>  		in6_ifa_put(ifp);

Please align idev and IFA_F_PERMANENT.

> @@ -542,7 +556,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
>  	int send_llinfo;
>  
>  	if (saddr == NULL) {
> -		if (ipv6_get_lladdr(dev, &addr_buf))
> +		if (ipv6_get_lladdr(dev, &addr_buf,
> +			(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
>  			return;
>  		saddr = &addr_buf;
>  	}

ditto... ("dev" and "(")

> +			   and optimistic) are false then we can just fail
> +			   dad now.
> +			*/
> +			type = ipv6_addr_type(saddr);			
> +			if (!((ifp->flags & IFA_F_OPTIMISTIC) && 
> +			    (type & IPV6_ADDR_UNICAST))) {
> +				addrconf_dad_failure(ifp); 
> +				return;
> +			}
>  		}
>  
>  		idev = ifp->idev;

hmm? Here, is saddr always unicast, isn't it?!

--yoshfuji

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-19 23:05 ` YOSHIFUJI Hideaki / 吉藤英明
@ 2007-01-20  1:41   ` Neil Horman
  2007-01-22 18:15   ` Neil Horman
  1 sibling, 0 replies; 71+ messages in thread
From: Neil Horman @ 2007-01-20  1:41 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: davem, kuznet, pekkas, jmorris, kaber, netdev

On Sat, Jan 20, 2007 at 08:05:07AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
> Hello.
> 
> In article <20070119212314.GA10748@hmsreliant.homelinux.net> (at Fri, 19 Jan 2007 16:23:14 -0500), Neil Horman <nhorman@tuxdriver.com> says:
> 
> > Patch to Implement IPv6 RFC 4429 (Optimistic Duplicate Address Detection).  In
> 
> Good work.  We will see if this would break core and basic ipv6 code.
> Dave, please hold on.
> 
Thank you.  I'll implement your requested changes and repost monday afternoon
Regards
Neil

> Some quick comments.
> 
> > --- a/include/net/ipv6.h
> > +++ b/include/net/ipv6.h
> > @@ -110,6 +110,7 @@ struct frag_hdr {
> >  /* sysctls */
> >  extern int sysctl_ipv6_bindv6only;
> >  extern int sysctl_mld_max_msf;
> > +extern int sysctl_optimistic_dad;
> >  
> :
> > diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> > index 2a7e461..f7afb2a 100644
> > --- a/net/ipv6/addrconf.c
> > +++ b/net/ipv6/addrconf.c
> > @@ -206,6 +206,8 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
> >  	.proxy_ndp		= 0,
> >  };
> >  
> > +int sysctl_optimistic_dad = 1;
> > +
> 
> Please put this into ipv6_devconf{} and make it per-interface variable.
> And I think default should be kept off (0).
> 
> >  /* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
> >  #if 0
> >  const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
> > @@ -830,7 +832,8 @@ retry:
> >  	ift = !max_addresses ||
> >  	      ipv6_count_addresses(idev) < max_addresses ? 
> >  		ipv6_add_addr(idev, &addr, tmp_plen,
> > -			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
> > +			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
> > +				IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
> >  	if (!ift || IS_ERR(ift)) {
> >  		in6_ifa_put(ifp);
> >  		in6_dev_put(idev);
> 
> Please align ipv6_addr_type and IFA_F_TEMPORARY
> 
> > @@ -1174,7 +1177,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
> >  }
> >  
> >  
> > -int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
> > +int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, 
> > +			unsigned char banned_flags)
> >  {
> >  	struct inet6_dev *idev;
> >  	int err = -EADDRNOTAVAIL;
> 
> Please align "struct net_device" and "unsigned char".
> 
> > @@ -1185,7 +1189,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
> >  
> >  		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 && !(ifp->flags&banned_flags)) {
> >  				ipv6_addr_copy(addr, &ifp->addr);
> >  				err = 0;
> >  				break;
> > @@ -1742,7 +1746,7 @@ ok:
> 
> It is not your fault, but please put a space around "&".
> 
> >  			if (!max_addresses ||
> >  			    ipv6_count_addresses(in6_dev) < max_addresses)
> >  				ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len,
> > -						    addr_type&IPV6_ADDR_SCOPE_MASK, 0);
> > +						    addr_type&IPV6_ADDR_SCOPE_MASK,0);
> >  
> >  			if (!ifp || IS_ERR(ifp)) {
> >  				in6_dev_put(in6_dev);
> 
> Please do no kill space after ",".
> 
> > @@ -2123,7 +2132,8 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr
> >  {
> >  	struct inet6_ifaddr * ifp;
> >  
> > -	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
> > +	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, 
> > +		IFA_F_PERMANENT|IFA_F_OPTIMISTIC);
> >  	if (!IS_ERR(ifp)) {
> >  		addrconf_dad_start(ifp, 0);
> >  		in6_ifa_put(ifp);
> 
> Please align idev and IFA_F_PERMANENT.
> 
> > @@ -542,7 +556,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
> >  	int send_llinfo;
> >  
> >  	if (saddr == NULL) {
> > -		if (ipv6_get_lladdr(dev, &addr_buf))
> > +		if (ipv6_get_lladdr(dev, &addr_buf,
> > +			(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
> >  			return;
> >  		saddr = &addr_buf;
> >  	}
> 
> ditto... ("dev" and "(")
> 
> > +			   and optimistic) are false then we can just fail
> > +			   dad now.
> > +			*/
> > +			type = ipv6_addr_type(saddr);			
> > +			if (!((ifp->flags & IFA_F_OPTIMISTIC) && 
> > +			    (type & IPV6_ADDR_UNICAST))) {
> > +				addrconf_dad_failure(ifp); 
> > +				return;
> > +			}
> >  		}
> >  
> >  		idev = ifp->idev;
> 
> hmm? Here, is saddr always unicast, isn't it?!
> 
> --yoshfuji

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-19 23:05 ` YOSHIFUJI Hideaki / 吉藤英明
  2007-01-20  1:41   ` Neil Horman
@ 2007-01-22 18:15   ` Neil Horman
  2007-01-22 18:39     ` Mika Penttilä
  2007-01-23  0:18     ` YOSHIFUJI Hideaki / 吉藤英明
  1 sibling, 2 replies; 71+ messages in thread
From: Neil Horman @ 2007-01-22 18:15 UTC (permalink / raw)
  To: yoshfuji; +Cc: davem, kuznet, pekkas, jmorris, kaber, netdev

On Sat, Jan 20, 2007 at 08:05:07AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
> Hello.
> 
> In article <20070119212314.GA10748@hmsreliant.homelinux.net> (at Fri, 19 Jan 2007 16:23:14 -0500), Neil Horman <nhorman@tuxdriver.com> says:
> 
> > Patch to Implement IPv6 RFC 4429 (Optimistic Duplicate Address Detection).  In
> 
> Good work.  We will see if this would break core and basic ipv6 code.
> Dave, please hold on.
> 
> Some quick comments.
> --yoshfuji
<snip>

Reposted patch, with your suggestions/corrections incorporated.  The only thing
I left alone was your last comment regarding the checking of saddr for being a
unicast address.  According to the RFC as I read it, its possible to receive
valid neighbor soliciations with a source address that is the unspecified
address, which I didn't think ipv6_addr_type flagged as being unicast.  Now it
may be that such NS messages are discarded before arriving at that recieve
routine, but I was figuring better safe than sorry.  If you're sufficiently
confident that we won't see non-unicast saddrs there, let me know and I'll
happily remove that as well.

Thanks & Regards
Neil

Signed-off-by: Neil Horman <nhorman@tuxdriver.com>


 include/linux/if_addr.h |    1 
 include/linux/ipv6.h    |    1 
 include/linux/sysctl.h  |    1 
 include/net/addrconf.h  |    4 ++-
 net/ipv6/addrconf.c     |   55 ++++++++++++++++++++++++++++++++++++++------
 net/ipv6/mcast.c        |    4 +--
 net/ipv6/ndisc.c        |   59 ++++++++++++++++++++++++++++++++++++++++--------
 7 files changed, 105 insertions(+), 20 deletions(-)



diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
index d557e4c..43f3bed 100644
--- a/include/linux/if_addr.h
+++ b/include/linux/if_addr.h
@@ -39,6 +39,7 @@ enum
 #define IFA_F_TEMPORARY		IFA_F_SECONDARY
 
 #define	IFA_F_NODAD		0x02
+#define IFA_F_OPTIMISTIC	0x04
 #define	IFA_F_HOMEADDRESS	0x10
 #define IFA_F_DEPRECATED	0x20
 #define IFA_F_TENTATIVE		0x40
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index f824113..1a8edc1 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -176,6 +176,7 @@ struct ipv6_devconf {
 	__s32		accept_ra_rt_info_max_plen;
 #endif
 #endif
+	__s32		use_optimistic_dad;
 	__s32		proxy_ndp;
 	void		*sysctl;
 };
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 81480e6..972a33a 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -570,6 +570,7 @@ enum {
 	NET_IPV6_RTR_PROBE_INTERVAL=21,
 	NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22,
 	NET_IPV6_PROXY_NDP=23,
+	NET_IPV6_OPTIMISTIC_DAD=24,
 	__NET_IPV6_MAX
 };
 
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 88df8fc..d248a19 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -73,7 +73,9 @@ extern int			ipv6_get_saddr(struct dst_entry *dst,
 extern int			ipv6_dev_get_saddr(struct net_device *dev, 
 					       struct in6_addr *daddr,
 					       struct in6_addr *saddr);
-extern int			ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
+extern int			ipv6_get_lladdr(struct net_device *dev, 
+						struct in6_addr *,
+						unsigned char banned_flags);
 extern int			ipv6_rcv_saddr_equal(const struct sock *sk, 
 						      const struct sock *sk2);
 extern void			addrconf_join_solict(struct net_device *dev,
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 2a7e461..316d771 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -830,7 +830,8 @@ retry:
 	ift = !max_addresses ||
 	      ipv6_count_addresses(idev) < max_addresses ? 
 		ipv6_add_addr(idev, &addr, tmp_plen,
-			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
+			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
+			      IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
 	if (!ift || IS_ERR(ift)) {
 		in6_ifa_put(ifp);
 		in6_dev_put(idev);
@@ -1174,7 +1175,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
 }
 
 
-int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
+int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, 
+		    unsigned char banned_flags)
 {
 	struct inet6_dev *idev;
 	int err = -EADDRNOTAVAIL;
@@ -1185,7 +1187,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
 
 		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 && !(ifp->flags & banned_flags)) {
 				ipv6_addr_copy(addr, &ifp->addr);
 				err = 0;
 				break;
@@ -1751,6 +1753,7 @@ ok:
 
 			update_lft = create = 1;
 			ifp->cstamp = jiffies;
+			ifp->flags |= IFA_F_OPTIMISTIC;
 			addrconf_dad_start(ifp, RTF_ADDRCONF|RTF_PREFIX_RT);
 		}
 
@@ -1945,7 +1948,11 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
 		ifp->prefered_lft = prefered_lft;
 		ifp->tstamp = jiffies;
 		spin_unlock_bh(&ifp->lock);
-
+		/*
+		 * Note that section 3.1 of RFC 4429 indicates
+		 * That the Optimistic flag should not be set for
+		 * manually configured addresses
+		 */
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
 		addrconf_verify(0);
@@ -2123,7 +2130,8 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr
 {
 	struct inet6_ifaddr * ifp;
 
-	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
+	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, 
+		            IFA_F_PERMANENT|IFA_F_OPTIMISTIC);
 	if (!IS_ERR(ifp)) {
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
@@ -2190,7 +2198,7 @@ ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
 {
 	struct in6_addr lladdr;
 
-	if (!ipv6_get_lladdr(link_dev, &lladdr)) {
+	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
 		addrconf_add_linklocal(idev, &lladdr);
 		return 0;
 	}
@@ -2537,8 +2545,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	struct inet6_dev *idev = ifp->idev;
 	struct net_device *dev = idev->dev;
 
+	if (!idev->cnf.use_optimistic_dad)
+		ifp->flags &= ~IFA_F_OPTIMISTIC;
+
 	addrconf_join_solict(dev, &ifp->addr);
 
+	/*
+	 * Optimistic nodes need to joing the anycast address
+	 * right away
+	 */
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addrconf_join_anycast(ifp);
+
 	if (ifp->prefix_len != 128 && (ifp->flags&IFA_F_PERMANENT))
 		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 0,
 					flags);
@@ -2553,7 +2571,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
 	    !(ifp->flags&IFA_F_TENTATIVE) ||
 	    ifp->flags & IFA_F_NODAD) {
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -2573,6 +2591,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 		addrconf_dad_stop(ifp);
 		return;
 	}
+
+	/*
+	 * Forwarding devices (routers) should not use
+	 * optimistic addresses
+	 * Nor should interfaces that don't know the 
+	 * Source address for their default gateway
+	 * RFC 4429 Sec 3.3
+	 */
+	if ((ipv6_devconf.forwarding) ||
+	   (ifp->rt == NULL))
+		ifp->flags &= ~IFA_F_OPTIMISTIC;
+
 	addrconf_dad_kick(ifp);
 	spin_unlock_bh(&ifp->lock);
 out:
@@ -2597,7 +2627,7 @@ static void addrconf_dad_timer(unsigned long data)
 		 * DAD was successful
 		 */
 
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -3918,6 +3948,15 @@ static struct addrconf_sysctl_table
 			.proc_handler	=	&proc_dointvec,
 		},
 		{
+			.ctl_name	=	NET_IPV6_OPTIMISTIC_DAD,
+			.procname       =       "use_optimistic_dad",
+			.data           =       &ipv6_devconf.use_optimistic_dad,
+			.maxlen         =       sizeof(int),
+			.mode           =       0644,
+			.proc_handler   =       &proc_dointvec,
+
+		},
+		{
 			.ctl_name	=	0,	/* sentinel */
 		}
 	},
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 882cde4..9c5273c 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
@@ -1789,7 +1789,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 6a9f616..9edbf9d 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -498,7 +498,21 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
         msg->icmph.icmp6_unused = 0;
         msg->icmph.icmp6_router    = router;
         msg->icmph.icmp6_solicited = solicited;
-        msg->icmph.icmp6_override  = override;
+	if (!ifp || !(ifp->flags & IFA_F_OPTIMISTIC))
+		msg->icmph.icmp6_override  = override;
+	else {
+		/*
+		 * We must clear the override flag on all
+		 * neighbor advertisements from source 
+		 * addresses that are OPTIMISTIC - RFC 4429
+		 * section 2.2
+		 */
+		if (override)
+			printk(KERN_WARNING
+				"Disallowing override flag for OPTIMISTIC addr\n");
+		msg->icmph.icmp6_override = 0;
+	}
+
 
         /* Set the target address. */
 	ipv6_addr_copy(&msg->target, solicited_addr);
@@ -542,7 +556,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
 	int send_llinfo;
 
 	if (saddr == NULL) {
-		if (ipv6_get_lladdr(dev, &addr_buf))
+		if (ipv6_get_lladdr(dev, &addr_buf,
+			            (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
 			return;
 		saddr = &addr_buf;
 	}
@@ -622,9 +637,20 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
         struct sk_buff *skb;
         struct icmp6hdr *hdr;
 	__u8 * opt;
+	struct inet6_ifaddr *ifp;
         int len;
 	int err;
 
+	/*
+	 * Check the source address.  If its OPTIMISTIC
+	 * and addr_len is non-zero (implying the sllao option)
+	 * then don't send the RS (RFC 4429, section 2.2)
+	 */
+	ifp = ipv6_get_ifaddr(saddr, dev, 1);
+
+	if ((!ifp) || ((ifp->flags & IFA_F_OPTIMISTIC) && dev->addr_len))
+		return;
+
 	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
 			dev->ifindex);
 
@@ -746,6 +772,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	int dad = ipv6_addr_any(saddr);
 	int inc;
 	int is_router;
+	int type;
 
 	if (ipv6_addr_is_multicast(&msg->target)) {
 		ND_PRINTK2(KERN_WARNING 
@@ -796,11 +823,13 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	inc = ipv6_addr_is_multicast(daddr);
 
 	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
-		if (ifp->flags & IFA_F_TENTATIVE) {
+
+		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
 			/* Address is tentative. If the source
-			   is unspecified address, it is someone
-			   does DAD, otherwise we ignore solicitations
-			   until DAD timer expires.
+			   is unspecified address, someone else
+			   is doing DAD, and if its not us, then
+			   we need to fail our own DAD
+			   RFC 4429 Sec 3.3
 			 */
 			if (!dad)
 				goto out;
@@ -816,8 +845,20 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 					goto out;
 				}
 			}
-			addrconf_dad_failure(ifp); 
-			return;
+
+			/* The one exception to the above rule about 
+			   optimistic addresses is that we need to always 
+			   respond to an NS from a unicast address if we are
+			   optimistic. RFC 4429 Sec 3.3.  If (unicast
+			   and optimistic) are false then we can just fail
+			   dad now.
+			*/
+			type = ipv6_addr_type(saddr);			
+			if (!((ifp->flags & IFA_F_OPTIMISTIC) && 
+			    (type & IPV6_ADDR_UNICAST))) {
+				addrconf_dad_failure(ifp); 
+				return;
+			}
 		}
 
 		idev = ifp->idev;
@@ -1406,7 +1447,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
 
 	dev = skb->dev;
 
-	if (ipv6_get_lladdr(dev, &saddr_buf)) {
+	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
 		ND_PRINTK2(KERN_WARNING
 			   "ICMPv6 Redirect: no link-local address on %s\n",
 			   dev->name);

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-22 18:15   ` Neil Horman
@ 2007-01-22 18:39     ` Mika Penttilä
  2007-01-22 19:45       ` Neil Horman
  2007-01-23  0:18     ` YOSHIFUJI Hideaki / 吉藤英明
  1 sibling, 1 reply; 71+ messages in thread
From: Mika Penttilä @ 2007-01-22 18:39 UTC (permalink / raw)
  To: Neil Horman; +Cc: yoshfuji, davem, kuznet, pekkas, jmorris, kaber, netdev

Neil Horman wrote:
> On Sat, Jan 20, 2007 at 08:05:07AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
>   
>> Hello.
>>
>> In article <20070119212314.GA10748@hmsreliant.homelinux.net> (at Fri, 19 Jan 2007 16:23:14 -0500), Neil Horman <nhorman@tuxdriver.com> says:
>>
>>     
>>> Patch to Implement IPv6 RFC 4429 (Optimistic Duplicate Address Detection).  In
>>>       
>> Good work.  We will see if this would break core and basic ipv6 code.
>> Dave, please hold on.
>>
>> Some quick comments.
>> --yoshfuji
>>     
> <snip>
>
> Reposted patch, with your suggestions/corrections incorporated.  The only thing
> I left alone was your last comment regarding the checking of saddr for being a
> unicast address.  According to the RFC as I read it, its possible to receive
> valid neighbor soliciations with a source address that is the unspecified
> address, which I didn't think ipv6_addr_type flagged as being unicast.  Now it
> may be that such NS messages are discarded before arriving at that recieve
> routine, but I was figuring better safe than sorry.  If you're sufficiently
> confident that we won't see non-unicast saddrs there, let me know and I'll
> happily remove that as well.
>
> Thanks & Regards
> Neil
>
> NTATIVE) {
> +
> +		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
>  			/* Address is tentative. If the source
> -			   is unspecified address, it is someone
> -			   does DAD, otherwise we ignore solicitations
> -			   until DAD timer expires.
> +			   is unspecified address, someone else
> +			   is doing DAD, and if its not us, then
> +			   we need to fail our own DAD
> +			   RFC 4429 Sec 3.3
>  			 */
>  			if (!dad)
>  				goto out;
> @@ -816,8 +845,20 @@ static void ndisc_recv_ns(struct sk_buff *skb)
>  					goto out;
>  				}
>  			}
> -			addrconf_dad_failure(ifp); 
> -			return;
> +
> +			/* The one exception to the above rule about 
> +			   optimistic addresses is that we need to always 
> +			   respond to an NS from a unicast address if we are
> +			   optimistic. RFC 4429 Sec 3.3.  If (unicast
> +			   and optimistic) are false then we can just fail
> +			   dad now.
> +			*/
> +			type = ipv6_addr_type(saddr);			
> +			if (!((ifp->flags & IFA_F_OPTIMISTIC) && 
> +			    (type & IPV6_ADDR_UNICAST))) {
> +				addrconf_dad_failure(ifp); 
> +				return;
> +			}
>  		}
>   
I think you should remove / modify the :
if (!dad)
    goto out;

which makes the rfc4429 tests not functional.

--Mika





^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-22 18:39     ` Mika Penttilä
@ 2007-01-22 19:45       ` Neil Horman
  2007-01-22 20:25         ` Vlad Yasevich
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-01-22 19:45 UTC (permalink / raw)
  To: Mika Penttilä
  Cc: yoshfuji, davem, kuznet, pekkas, jmorris, kaber, netdev

On Mon, Jan 22, 2007 at 08:39:24PM +0200, Mika Penttilä wrote:
> Neil Horman wrote:
><snip >  
> I think you should remove / modify the :
> if (!dad)
>    goto out;
> 
> which makes the rfc4429 tests not functional.
> 
> --Mika
> 

Yeah, I think you're right.  In fact, as I look at it more closely I think that
logic for NS message recives is a bit confused overall.  Section 3.3 of RFC 4429
says that I should fail DAD if I'm optimistic and I get an NS from the
unspecified address, but I must reply to an NS from a unicast address.  The way
the logic is written, I was going to fail dad if the address was just tentative
(i.e. odad was disabled), which is a deviation from the nominal behavior.  And
of course, as you mention, the (!dad) clause kills the odad tests (although it
is still required if we are not an optimistic address).  This updated patch
should take care of all that, as well as make that logic more clear

Thanks & Regards
Neil


Signed-off-by: Neil Horman <nhorman@tuxdriver.com>


 include/linux/if_addr.h |    1 
 include/linux/ipv6.h    |    1 
 include/linux/sysctl.h  |    1 
 include/net/addrconf.h  |    4 ++-
 net/ipv6/addrconf.c     |   55 ++++++++++++++++++++++++++++++++++++------
 net/ipv6/mcast.c        |    4 +--
 net/ipv6/ndisc.c        |   62 +++++++++++++++++++++++++++++++++++++-----------
 7 files changed, 104 insertions(+), 24 deletions(-)



diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
index d557e4c..43f3bed 100644
--- a/include/linux/if_addr.h
+++ b/include/linux/if_addr.h
@@ -39,6 +39,7 @@ enum
 #define IFA_F_TEMPORARY		IFA_F_SECONDARY
 
 #define	IFA_F_NODAD		0x02
+#define IFA_F_OPTIMISTIC	0x04
 #define	IFA_F_HOMEADDRESS	0x10
 #define IFA_F_DEPRECATED	0x20
 #define IFA_F_TENTATIVE		0x40
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index f824113..1a8edc1 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -176,6 +176,7 @@ struct ipv6_devconf {
 	__s32		accept_ra_rt_info_max_plen;
 #endif
 #endif
+	__s32		use_optimistic_dad;
 	__s32		proxy_ndp;
 	void		*sysctl;
 };
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 81480e6..972a33a 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -570,6 +570,7 @@ enum {
 	NET_IPV6_RTR_PROBE_INTERVAL=21,
 	NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22,
 	NET_IPV6_PROXY_NDP=23,
+	NET_IPV6_OPTIMISTIC_DAD=24,
 	__NET_IPV6_MAX
 };
 
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 88df8fc..d248a19 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -73,7 +73,9 @@ extern int			ipv6_get_saddr(struct dst_entry *dst,
 extern int			ipv6_dev_get_saddr(struct net_device *dev, 
 					       struct in6_addr *daddr,
 					       struct in6_addr *saddr);
-extern int			ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
+extern int			ipv6_get_lladdr(struct net_device *dev, 
+						struct in6_addr *,
+						unsigned char banned_flags);
 extern int			ipv6_rcv_saddr_equal(const struct sock *sk, 
 						      const struct sock *sk2);
 extern void			addrconf_join_solict(struct net_device *dev,
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 2a7e461..316d771 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -830,7 +830,8 @@ retry:
 	ift = !max_addresses ||
 	      ipv6_count_addresses(idev) < max_addresses ? 
 		ipv6_add_addr(idev, &addr, tmp_plen,
-			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
+			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
+			      IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
 	if (!ift || IS_ERR(ift)) {
 		in6_ifa_put(ifp);
 		in6_dev_put(idev);
@@ -1174,7 +1175,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
 }
 
 
-int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
+int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, 
+		    unsigned char banned_flags)
 {
 	struct inet6_dev *idev;
 	int err = -EADDRNOTAVAIL;
@@ -1185,7 +1187,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
 
 		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 && !(ifp->flags & banned_flags)) {
 				ipv6_addr_copy(addr, &ifp->addr);
 				err = 0;
 				break;
@@ -1751,6 +1753,7 @@ ok:
 
 			update_lft = create = 1;
 			ifp->cstamp = jiffies;
+			ifp->flags |= IFA_F_OPTIMISTIC;
 			addrconf_dad_start(ifp, RTF_ADDRCONF|RTF_PREFIX_RT);
 		}
 
@@ -1945,7 +1948,11 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
 		ifp->prefered_lft = prefered_lft;
 		ifp->tstamp = jiffies;
 		spin_unlock_bh(&ifp->lock);
-
+		/*
+		 * Note that section 3.1 of RFC 4429 indicates
+		 * That the Optimistic flag should not be set for
+		 * manually configured addresses
+		 */
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
 		addrconf_verify(0);
@@ -2123,7 +2130,8 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr
 {
 	struct inet6_ifaddr * ifp;
 
-	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
+	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, 
+		            IFA_F_PERMANENT|IFA_F_OPTIMISTIC);
 	if (!IS_ERR(ifp)) {
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
@@ -2190,7 +2198,7 @@ ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
 {
 	struct in6_addr lladdr;
 
-	if (!ipv6_get_lladdr(link_dev, &lladdr)) {
+	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
 		addrconf_add_linklocal(idev, &lladdr);
 		return 0;
 	}
@@ -2537,8 +2545,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	struct inet6_dev *idev = ifp->idev;
 	struct net_device *dev = idev->dev;
 
+	if (!idev->cnf.use_optimistic_dad)
+		ifp->flags &= ~IFA_F_OPTIMISTIC;
+
 	addrconf_join_solict(dev, &ifp->addr);
 
+	/*
+	 * Optimistic nodes need to joing the anycast address
+	 * right away
+	 */
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addrconf_join_anycast(ifp);
+
 	if (ifp->prefix_len != 128 && (ifp->flags&IFA_F_PERMANENT))
 		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 0,
 					flags);
@@ -2553,7 +2571,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
 	    !(ifp->flags&IFA_F_TENTATIVE) ||
 	    ifp->flags & IFA_F_NODAD) {
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -2573,6 +2591,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 		addrconf_dad_stop(ifp);
 		return;
 	}
+
+	/*
+	 * Forwarding devices (routers) should not use
+	 * optimistic addresses
+	 * Nor should interfaces that don't know the 
+	 * Source address for their default gateway
+	 * RFC 4429 Sec 3.3
+	 */
+	if ((ipv6_devconf.forwarding) ||
+	   (ifp->rt == NULL))
+		ifp->flags &= ~IFA_F_OPTIMISTIC;
+
 	addrconf_dad_kick(ifp);
 	spin_unlock_bh(&ifp->lock);
 out:
@@ -2597,7 +2627,7 @@ static void addrconf_dad_timer(unsigned long data)
 		 * DAD was successful
 		 */
 
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -3918,6 +3948,15 @@ static struct addrconf_sysctl_table
 			.proc_handler	=	&proc_dointvec,
 		},
 		{
+			.ctl_name	=	NET_IPV6_OPTIMISTIC_DAD,
+			.procname       =       "use_optimistic_dad",
+			.data           =       &ipv6_devconf.use_optimistic_dad,
+			.maxlen         =       sizeof(int),
+			.mode           =       0644,
+			.proc_handler   =       &proc_dointvec,
+
+		},
+		{
 			.ctl_name	=	0,	/* sentinel */
 		}
 	},
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 882cde4..9c5273c 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
@@ -1789,7 +1789,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 6a9f616..bb872ef 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -498,7 +498,21 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
         msg->icmph.icmp6_unused = 0;
         msg->icmph.icmp6_router    = router;
         msg->icmph.icmp6_solicited = solicited;
-        msg->icmph.icmp6_override  = override;
+	if (!ifp || !(ifp->flags & IFA_F_OPTIMISTIC))
+		msg->icmph.icmp6_override  = override;
+	else {
+		/*
+		 * We must clear the override flag on all
+		 * neighbor advertisements from source 
+		 * addresses that are OPTIMISTIC - RFC 4429
+		 * section 2.2
+		 */
+		if (override)
+			printk(KERN_WARNING
+				"Disallowing override flag for OPTIMISTIC addr\n");
+		msg->icmph.icmp6_override = 0;
+	}
+
 
         /* Set the target address. */
 	ipv6_addr_copy(&msg->target, solicited_addr);
@@ -542,7 +556,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
 	int send_llinfo;
 
 	if (saddr == NULL) {
-		if (ipv6_get_lladdr(dev, &addr_buf))
+		if (ipv6_get_lladdr(dev, &addr_buf,
+			            (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
 			return;
 		saddr = &addr_buf;
 	}
@@ -622,9 +637,20 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
         struct sk_buff *skb;
         struct icmp6hdr *hdr;
 	__u8 * opt;
+	struct inet6_ifaddr *ifp;
         int len;
 	int err;
 
+	/*
+	 * Check the source address.  If its OPTIMISTIC
+	 * and addr_len is non-zero (implying the sllao option)
+	 * then don't send the RS (RFC 4429, section 2.2)
+	 */
+	ifp = ipv6_get_ifaddr(saddr, dev, 1);
+
+	if ((!ifp) || ((ifp->flags & IFA_F_OPTIMISTIC) && dev->addr_len))
+		return;
+
 	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
 			dev->ifindex);
 
@@ -746,6 +772,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	int dad = ipv6_addr_any(saddr);
 	int inc;
 	int is_router;
+	int type;
 
 	if (ipv6_addr_is_multicast(&msg->target)) {
 		ND_PRINTK2(KERN_WARNING 
@@ -796,14 +823,8 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	inc = ipv6_addr_is_multicast(daddr);
 
 	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
-		if (ifp->flags & IFA_F_TENTATIVE) {
-			/* Address is tentative. If the source
-			   is unspecified address, it is someone
-			   does DAD, otherwise we ignore solicitations
-			   until DAD timer expires.
-			 */
-			if (!dad)
-				goto out;
+
+		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
 			if (dev->type == ARPHRD_IEEE802_TR) {
 				unsigned char *sadr = skb->mac.raw;
 				if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
@@ -816,8 +837,23 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 					goto out;
 				}
 			}
-			addrconf_dad_failure(ifp); 
-			return;
+
+			/* The one exception to the above rule about 
+			   optimistic addresses is that we need to always 
+			   respond to an NS from a unicast address if we are
+			   optimistic. RFC 4429 Sec 3.3.  If (unicast
+			   and optimistic) are false then we can just fail
+			   dad now.
+			*/
+			if (ifp->flags & IFA_F_OPTIMISTIC) {
+				type = ipv6_addr_type(saddr);			
+				if (!(type & IPV6_ADDR_UNICAST)) {
+					addrconf_dad_failure(ifp); 
+					goto out;
+				}
+			} else
+				if (!dad)
+					goto out;
 		}
 
 		idev = ifp->idev;
@@ -1406,7 +1442,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
 
 	dev = skb->dev;
 
-	if (ipv6_get_lladdr(dev, &saddr_buf)) {
+	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
 		ND_PRINTK2(KERN_WARNING
 			   "ICMPv6 Redirect: no link-local address on %s\n",
 			   dev->name);

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-22 19:45       ` Neil Horman
@ 2007-01-22 20:25         ` Vlad Yasevich
  2007-01-23 18:36           ` Neil Horman
  0 siblings, 1 reply; 71+ messages in thread
From: Vlad Yasevich @ 2007-01-22 20:25 UTC (permalink / raw)
  To: Neil Horman
  Cc: Mika Penttilä,
	yoshfuji, davem, kuznet, pekkas, jmorris, kaber, netdev

Hi Neil

I don't this is still right...

> @@ -746,6 +772,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
>  	int dad = ipv6_addr_any(saddr);
>  	int inc;
>  	int is_router;
> +	int type;
>  
>  	if (ipv6_addr_is_multicast(&msg->target)) {
>  		ND_PRINTK2(KERN_WARNING 
> @@ -796,14 +823,8 @@ static void ndisc_recv_ns(struct sk_buff *skb)
>  	inc = ipv6_addr_is_multicast(daddr);
>  
>  	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
> -		if (ifp->flags & IFA_F_TENTATIVE) {
> -			/* Address is tentative. If the source
> -			   is unspecified address, it is someone
> -			   does DAD, otherwise we ignore solicitations
> -			   until DAD timer expires.
> -			 */
> -			if (!dad)
> -				goto out;
> +
> +		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
>  			if (dev->type == ARPHRD_IEEE802_TR) {
>  				unsigned char *sadr = skb->mac.raw;
>  				if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
> @@ -816,8 +837,23 @@ static void ndisc_recv_ns(struct sk_buff *skb)
>  					goto out;
>  				}
>  			}

First, you do looped packet detection for all packets, not just DAD.

> -			addrconf_dad_failure(ifp); 
> -			return;
> +
> +			/* The one exception to the above rule about 
> +			   optimistic addresses is that we need to always 
> +			   respond to an NS from a unicast address if we are
> +			   optimistic. RFC 4429 Sec 3.3.  If (unicast
> +			   and optimistic) are false then we can just fail
> +			   dad now.
> +			*/
> +			if (ifp->flags & IFA_F_OPTIMISTIC) {
> +				type = ipv6_addr_type(saddr);			
> +				if (!(type & IPV6_ADDR_UNICAST)) {
> +					addrconf_dad_failure(ifp); 
> +					goto out;
> +				}
> +			} else
> +				if (!dad)
> +					goto out;


Second, you fail dad in the OPTIMISTIC case, but not the regular case, which should also fail
if this is a DAD packet.

I think the following is what you want (totally untested):

	if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {

		if (dad) {
			/* We are processing a DAD packet for a tentative address.
			 * Make sure that this was not one of our NSs looped back
			 * to us.
			 */
			if (dev->type== ARPHDR_IEEE802_TR) {
				..... blah ...
			}

			/* Fail DAD since we are colliding with someout out there*/
			addrconf_dad_failure(ifp);
		} else {
			/* This is not a DAD neighbor solicitation.  If we
			 * are OPTIMISTIC, we'll respond with a NA.  Otherwise
			 * we'll ignore the packet.
			 */
			if (!(ifp->flags & IFA_F_OPTIMISTIC))
				goto out
		}
	}
	idef = ifp->idev;

-vlad

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-22 18:15   ` Neil Horman
  2007-01-22 18:39     ` Mika Penttilä
@ 2007-01-23  0:18     ` YOSHIFUJI Hideaki / 吉藤英明
  2007-01-23 20:51       ` Neil Horman
  1 sibling, 1 reply; 71+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2007-01-23  0:18 UTC (permalink / raw)
  To: nhorman; +Cc: davem, kuznet, pekkas, jmorris, kaber, netdev, yoshfuji

Hello.

In article <20070122181528.GD21059@hmsreliant.homelinux.net> (at Mon, 22 Jan 2007 13:15:28 -0500), Neil Horman <nhorman@tuxdriver.com> says:

> Reposted patch, with your suggestions/corrections incorporated.  The only thing
> I left alone was your last comment regarding the checking of saddr for being a
> unicast address.  According to the RFC as I read it, its possible to receive
> valid neighbor soliciations with a source address that is the unspecified
> address, which I didn't think ipv6_addr_type flagged as being unicast.  Now it
> may be that such NS messages are discarded before arriving at that recieve
> routine, but I was figuring better safe than sorry.  If you're sufficiently
> confident that we won't see non-unicast saddrs there, let me know and I'll
> happily remove that as well.

Okay, right, we can see unspecified source address.
However, we have "dad" variable.

More comments.

> +++ b/include/linux/ipv6.h
> @@ -176,6 +176,7 @@ struct ipv6_devconf {
>  	__s32		accept_ra_rt_info_max_plen;
>  #endif
>  #endif
> +	__s32		use_optimistic_dad;
>  	__s32		proxy_ndp;
>  	void		*sysctl;
>  };

Please do not insert between variables but add to the right before the
"sysctl" member.

> diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
> index 81480e6..972a33a 100644
> --- a/include/linux/sysctl.h
> +++ b/include/linux/sysctl.h
> @@ -570,6 +570,7 @@ enum {
>  	NET_IPV6_RTR_PROBE_INTERVAL=21,
>  	NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22,
>  	NET_IPV6_PROXY_NDP=23,
> +	NET_IPV6_OPTIMISTIC_DAD=24,
>  	__NET_IPV6_MAX
>  };
>  
:
> @@ -3918,6 +3948,15 @@ static struct addrconf_sysctl_table
>  			.proc_handler	=	&proc_dointvec,
>  		},
>  		{
> +			.ctl_name	=	NET_IPV6_OPTIMISTIC_DAD,
> +			.procname       =       "use_optimistic_dad",
> +			.data           =       &ipv6_devconf.use_optimistic_dad,
> +			.maxlen         =       sizeof(int),
> +			.mode           =       0644,
> +			.proc_handler   =       &proc_dointvec,
> +
> +		},
> +		{
>  			.ctl_name	=	0,	/* sentinel */
>  		}
>  	},

Please use similar names (sysctl enum, member name and sysctl name(s)); e.g.
NET_IPV6_OPTIMISTIC_DAD, optimistic_dad, "optimistic_dad".

You forgot adding DEVCONF_OPTIMISTIC_DAD in include/linux/ipv6.h
and net/ipv6/addrconf.c:ipv6_store_devconf().

:
> @@ -746,6 +772,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
>  	int dad = ipv6_addr_any(saddr);
>  	int inc;
>  	int is_router;
> +	int type;
>  
>  	if (ipv6_addr_is_multicast(&msg->target)) {
>  		ND_PRINTK2(KERN_WARNING 

Here, "dad" is true if the source is unspecified address.  So,

> @@ -816,8 +845,20 @@ static void ndisc_recv_ns(struct sk_buff *skb)
>  					goto out;
>  				}
>  			}
> -			addrconf_dad_failure(ifp); 
> -			return;
> +
> +			/* The one exception to the above rule about 
> +			   optimistic addresses is that we need to always 
> +			   respond to an NS from a unicast address if we are
> +			   optimistic. RFC 4429 Sec 3.3.  If (unicast
> +			   and optimistic) are false then we can just fail
> +			   dad now.
> +			*/
> +			type = ipv6_addr_type(saddr);			
> +			if (!((ifp->flags & IFA_F_OPTIMISTIC) && 
> +			    (type & IPV6_ADDR_UNICAST))) {
> +				addrconf_dad_failure(ifp); 
> +				return;
> +			}
>  		}
>  
>  		idev = ifp->idev;

You can say,
			if (dad || !(ifp->flags & IFA_F_OPTIMISTIC)) {
				addrconf_dad_failure(ifp);
				return;
			}

Regards,

-- 
YOSHIFUJI Hideaki @ USAGI Project  <yoshfuji@linux-ipv6.org>
GPG-FP  : 9022 65EB 1ECF 3AD1 0BDF  80D8 4807 F894 E062 0EEA

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-22 20:25         ` Vlad Yasevich
@ 2007-01-23 18:36           ` Neil Horman
  2007-01-23 19:27             ` Vlad Yasevich
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-01-23 18:36 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: Mika Penttilä,
	yoshfuji, davem, kuznet, pekkas, jmorris, kaber, netdev

On Mon, Jan 22, 2007 at 03:25:39PM -0500, Vlad Yasevich wrote:
> Hi Neil
<snip>

Yeah, I think your right.  I missed the implication of testing for (!dad) at the
top of that clause.  I think we could accomplish the same thing by moving my
additions to the top of the clause, but I think your logic reads more cleanly.
New patch attached

Regards
Neil


Signed-off-by: Neil Horman <nhorman@tuxdriver.com>

 include/linux/if_addr.h |    1 
 include/linux/ipv6.h    |    1 
 include/linux/sysctl.h  |    1 
 include/net/addrconf.h  |    4 +-
 net/ipv6/addrconf.c     |   55 +++++++++++++++++++++++++++-----
 net/ipv6/mcast.c        |    4 +-
 net/ipv6/ndisc.c        |   82 +++++++++++++++++++++++++++++++++++-------------
 7 files changed, 115 insertions(+), 33 deletions(-)



diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
index d557e4c..43f3bed 100644
--- a/include/linux/if_addr.h
+++ b/include/linux/if_addr.h
@@ -39,6 +39,7 @@ enum
 #define IFA_F_TEMPORARY		IFA_F_SECONDARY
 
 #define	IFA_F_NODAD		0x02
+#define IFA_F_OPTIMISTIC	0x04
 #define	IFA_F_HOMEADDRESS	0x10
 #define IFA_F_DEPRECATED	0x20
 #define IFA_F_TENTATIVE		0x40
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index f824113..1a8edc1 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -176,6 +176,7 @@ struct ipv6_devconf {
 	__s32		accept_ra_rt_info_max_plen;
 #endif
 #endif
+	__s32		use_optimistic_dad;
 	__s32		proxy_ndp;
 	void		*sysctl;
 };
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 81480e6..972a33a 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -570,6 +570,7 @@ enum {
 	NET_IPV6_RTR_PROBE_INTERVAL=21,
 	NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22,
 	NET_IPV6_PROXY_NDP=23,
+	NET_IPV6_OPTIMISTIC_DAD=24,
 	__NET_IPV6_MAX
 };
 
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 88df8fc..d248a19 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -73,7 +73,9 @@ extern int			ipv6_get_saddr(struct dst_entry *dst,
 extern int			ipv6_dev_get_saddr(struct net_device *dev, 
 					       struct in6_addr *daddr,
 					       struct in6_addr *saddr);
-extern int			ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
+extern int			ipv6_get_lladdr(struct net_device *dev, 
+						struct in6_addr *,
+						unsigned char banned_flags);
 extern int			ipv6_rcv_saddr_equal(const struct sock *sk, 
 						      const struct sock *sk2);
 extern void			addrconf_join_solict(struct net_device *dev,
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 2a7e461..316d771 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -830,7 +830,8 @@ retry:
 	ift = !max_addresses ||
 	      ipv6_count_addresses(idev) < max_addresses ? 
 		ipv6_add_addr(idev, &addr, tmp_plen,
-			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
+			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
+			      IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
 	if (!ift || IS_ERR(ift)) {
 		in6_ifa_put(ifp);
 		in6_dev_put(idev);
@@ -1174,7 +1175,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
 }
 
 
-int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
+int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, 
+		    unsigned char banned_flags)
 {
 	struct inet6_dev *idev;
 	int err = -EADDRNOTAVAIL;
@@ -1185,7 +1187,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
 
 		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 && !(ifp->flags & banned_flags)) {
 				ipv6_addr_copy(addr, &ifp->addr);
 				err = 0;
 				break;
@@ -1751,6 +1753,7 @@ ok:
 
 			update_lft = create = 1;
 			ifp->cstamp = jiffies;
+			ifp->flags |= IFA_F_OPTIMISTIC;
 			addrconf_dad_start(ifp, RTF_ADDRCONF|RTF_PREFIX_RT);
 		}
 
@@ -1945,7 +1948,11 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
 		ifp->prefered_lft = prefered_lft;
 		ifp->tstamp = jiffies;
 		spin_unlock_bh(&ifp->lock);
-
+		/*
+		 * Note that section 3.1 of RFC 4429 indicates
+		 * That the Optimistic flag should not be set for
+		 * manually configured addresses
+		 */
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
 		addrconf_verify(0);
@@ -2123,7 +2130,8 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr
 {
 	struct inet6_ifaddr * ifp;
 
-	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
+	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, 
+		            IFA_F_PERMANENT|IFA_F_OPTIMISTIC);
 	if (!IS_ERR(ifp)) {
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
@@ -2190,7 +2198,7 @@ ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
 {
 	struct in6_addr lladdr;
 
-	if (!ipv6_get_lladdr(link_dev, &lladdr)) {
+	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
 		addrconf_add_linklocal(idev, &lladdr);
 		return 0;
 	}
@@ -2537,8 +2545,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	struct inet6_dev *idev = ifp->idev;
 	struct net_device *dev = idev->dev;
 
+	if (!idev->cnf.use_optimistic_dad)
+		ifp->flags &= ~IFA_F_OPTIMISTIC;
+
 	addrconf_join_solict(dev, &ifp->addr);
 
+	/*
+	 * Optimistic nodes need to joing the anycast address
+	 * right away
+	 */
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addrconf_join_anycast(ifp);
+
 	if (ifp->prefix_len != 128 && (ifp->flags&IFA_F_PERMANENT))
 		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 0,
 					flags);
@@ -2553,7 +2571,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
 	    !(ifp->flags&IFA_F_TENTATIVE) ||
 	    ifp->flags & IFA_F_NODAD) {
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -2573,6 +2591,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 		addrconf_dad_stop(ifp);
 		return;
 	}
+
+	/*
+	 * Forwarding devices (routers) should not use
+	 * optimistic addresses
+	 * Nor should interfaces that don't know the 
+	 * Source address for their default gateway
+	 * RFC 4429 Sec 3.3
+	 */
+	if ((ipv6_devconf.forwarding) ||
+	   (ifp->rt == NULL))
+		ifp->flags &= ~IFA_F_OPTIMISTIC;
+
 	addrconf_dad_kick(ifp);
 	spin_unlock_bh(&ifp->lock);
 out:
@@ -2597,7 +2627,7 @@ static void addrconf_dad_timer(unsigned long data)
 		 * DAD was successful
 		 */
 
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -3918,6 +3948,15 @@ static struct addrconf_sysctl_table
 			.proc_handler	=	&proc_dointvec,
 		},
 		{
+			.ctl_name	=	NET_IPV6_OPTIMISTIC_DAD,
+			.procname       =       "use_optimistic_dad",
+			.data           =       &ipv6_devconf.use_optimistic_dad,
+			.maxlen         =       sizeof(int),
+			.mode           =       0644,
+			.proc_handler   =       &proc_dointvec,
+
+		},
+		{
 			.ctl_name	=	0,	/* sentinel */
 		}
 	},
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 882cde4..9c5273c 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
@@ -1789,7 +1789,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 6a9f616..71469b8 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -498,7 +498,21 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
         msg->icmph.icmp6_unused = 0;
         msg->icmph.icmp6_router    = router;
         msg->icmph.icmp6_solicited = solicited;
-        msg->icmph.icmp6_override  = override;
+	if (!ifp || !(ifp->flags & IFA_F_OPTIMISTIC))
+		msg->icmph.icmp6_override  = override;
+	else {
+		/*
+		 * We must clear the override flag on all
+		 * neighbor advertisements from source 
+		 * addresses that are OPTIMISTIC - RFC 4429
+		 * section 2.2
+		 */
+		if (override)
+			printk(KERN_WARNING
+				"Disallowing override flag for OPTIMISTIC addr\n");
+		msg->icmph.icmp6_override = 0;
+	}
+
 
         /* Set the target address. */
 	ipv6_addr_copy(&msg->target, solicited_addr);
@@ -542,7 +556,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
 	int send_llinfo;
 
 	if (saddr == NULL) {
-		if (ipv6_get_lladdr(dev, &addr_buf))
+		if (ipv6_get_lladdr(dev, &addr_buf,
+			            (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
 			return;
 		saddr = &addr_buf;
 	}
@@ -622,9 +637,20 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
         struct sk_buff *skb;
         struct icmp6hdr *hdr;
 	__u8 * opt;
+	struct inet6_ifaddr *ifp;
         int len;
 	int err;
 
+	/*
+	 * Check the source address.  If its OPTIMISTIC
+	 * and addr_len is non-zero (implying the sllao option)
+	 * then don't send the RS (RFC 4429, section 2.2)
+	 */
+	ifp = ipv6_get_ifaddr(saddr, dev, 1);
+
+	if ((!ifp) || ((ifp->flags & IFA_F_OPTIMISTIC) && dev->addr_len))
+		return;
+
 	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
 			dev->ifindex);
 
@@ -746,6 +772,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	int dad = ipv6_addr_any(saddr);
 	int inc;
 	int is_router;
+	int type;
 
 	if (ipv6_addr_is_multicast(&msg->target)) {
 		ND_PRINTK2(KERN_WARNING 
@@ -796,28 +823,39 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	inc = ipv6_addr_is_multicast(daddr);
 
 	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
-		if (ifp->flags & IFA_F_TENTATIVE) {
-			/* Address is tentative. If the source
-			   is unspecified address, it is someone
-			   does DAD, otherwise we ignore solicitations
-			   until DAD timer expires.
-			 */
-			if (!dad)
+
+		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
+			if (dad) {
+				if (dev->type == ARPHRD_IEEE802_TR) {
+					unsigned char *sadr = skb->mac.raw;
+					if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
+					    sadr[9] == dev->dev_addr[1] &&
+					    sadr[10] == dev->dev_addr[2] &&
+					    sadr[11] == dev->dev_addr[3] &&
+					    sadr[12] == dev->dev_addr[4] &&
+					    sadr[13] == dev->dev_addr[5]) {
+						/* looped-back to us */
+						goto out;
+					}
+				}
+
+				/*
+				 * We are colliding with another node
+				 * who is doing DAD
+				 * so fail our DAD process
+				 */
+				addrconf_dad_failure(ifp);
 				goto out;
-			if (dev->type == ARPHRD_IEEE802_TR) {
-				unsigned char *sadr = skb->mac.raw;
-				if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
-				    sadr[9] == dev->dev_addr[1] &&
-				    sadr[10] == dev->dev_addr[2] &&
-				    sadr[11] == dev->dev_addr[3] &&
-				    sadr[12] == dev->dev_addr[4] &&
-				    sadr[13] == dev->dev_addr[5]) {
-					/* looped-back to us */
+			} else {
+				/*
+				 * This is not a dad solicitation, meaning we may
+				 * need to respond to it, if we are 
+				 * an optimistic node, go ahead, otherwise 
+				 * ignore it
+				 */
+				if (!(ifp->flags & IFA_F_OPTIMISTIC))
 					goto out;
-				}
 			}
-			addrconf_dad_failure(ifp); 
-			return;
 		}
 
 		idev = ifp->idev;
@@ -1406,7 +1444,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
 
 	dev = skb->dev;
 
-	if (ipv6_get_lladdr(dev, &saddr_buf)) {
+	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
 		ND_PRINTK2(KERN_WARNING
 			   "ICMPv6 Redirect: no link-local address on %s\n",
 			   dev->name);

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-23 18:36           ` Neil Horman
@ 2007-01-23 19:27             ` Vlad Yasevich
  0 siblings, 0 replies; 71+ messages in thread
From: Vlad Yasevich @ 2007-01-23 19:27 UTC (permalink / raw)
  To: Neil Horman
  Cc: Mika Penttilä,
	yoshfuji, davem, kuznet, pekkas, jmorris, kaber, netdev

Hi Neil

Neil Horman wrote:
> +				/*
> +				 * This is not a dad solicitation, meaning we may
> +				 * need to respond to it, if we are 
> +				 * an optimistic node, go ahead, otherwise 
> +				 * ignore it

Nit.  Can you rephrase the comment just a bit.  It seems a bit of a run-on sentence...

The other comment from Yoshifuji-san still apply.

Thanks
-vlad


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-23  0:18     ` YOSHIFUJI Hideaki / 吉藤英明
@ 2007-01-23 20:51       ` Neil Horman
  2007-01-25  1:54         ` Sridhar Samudrala
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-01-23 20:51 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: davem, kuznet, pekkas, jmorris, kaber, netdev

On Tue, Jan 23, 2007 at 09:18:20AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
> Hello.
<snip>

New patch attached, incorporating Yoshijui and Vlads latest comments.  I didn't
follow guidance on the ndisc_recv_ns comment, Yoshifuji, since Vlad had already
suggested an alternate solution in a previous post, but from looking at them
both, they should be equivalent.

Thanks & Regards
Neil

Signed-off-by: Neil Horman <nhorman@tuxdriver.com>


 include/linux/if_addr.h |    1 
 include/linux/ipv6.h    |    2 +
 include/linux/sysctl.h  |    1 
 include/net/addrconf.h  |    4 +-
 net/ipv6/addrconf.c     |   56 ++++++++++++++++++++++++++++----
 net/ipv6/mcast.c        |    4 +-
 net/ipv6/ndisc.c        |   82 +++++++++++++++++++++++++++++++++++-------------
 7 files changed, 117 insertions(+), 33 deletions(-)


diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
index d557e4c..43f3bed 100644
--- a/include/linux/if_addr.h
+++ b/include/linux/if_addr.h
@@ -39,6 +39,7 @@ enum
 #define IFA_F_TEMPORARY		IFA_F_SECONDARY
 
 #define	IFA_F_NODAD		0x02
+#define IFA_F_OPTIMISTIC	0x04
 #define	IFA_F_HOMEADDRESS	0x10
 #define IFA_F_DEPRECATED	0x20
 #define IFA_F_TENTATIVE		0x40
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index f824113..5d37abf 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -177,6 +177,7 @@ struct ipv6_devconf {
 #endif
 #endif
 	__s32		proxy_ndp;
+	__s32		optimistic_dad;
 	void		*sysctl;
 };
 
@@ -205,6 +206,7 @@ enum {
 	DEVCONF_RTR_PROBE_INTERVAL,
 	DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN,
 	DEVCONF_PROXY_NDP,
+	DEVCONF_OPTIMISTIC_DAD,
 	DEVCONF_MAX
 };
 
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 81480e6..972a33a 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -570,6 +570,7 @@ enum {
 	NET_IPV6_RTR_PROBE_INTERVAL=21,
 	NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22,
 	NET_IPV6_PROXY_NDP=23,
+	NET_IPV6_OPTIMISTIC_DAD=24,
 	__NET_IPV6_MAX
 };
 
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 88df8fc..d248a19 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -73,7 +73,9 @@ extern int			ipv6_get_saddr(struct dst_entry *dst,
 extern int			ipv6_dev_get_saddr(struct net_device *dev, 
 					       struct in6_addr *daddr,
 					       struct in6_addr *saddr);
-extern int			ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
+extern int			ipv6_get_lladdr(struct net_device *dev, 
+						struct in6_addr *,
+						unsigned char banned_flags);
 extern int			ipv6_rcv_saddr_equal(const struct sock *sk, 
 						      const struct sock *sk2);
 extern void			addrconf_join_solict(struct net_device *dev,
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 2a7e461..d2b01ec 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -830,7 +830,8 @@ retry:
 	ift = !max_addresses ||
 	      ipv6_count_addresses(idev) < max_addresses ? 
 		ipv6_add_addr(idev, &addr, tmp_plen,
-			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
+			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
+			      IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
 	if (!ift || IS_ERR(ift)) {
 		in6_ifa_put(ifp);
 		in6_dev_put(idev);
@@ -1174,7 +1175,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
 }
 
 
-int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
+int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, 
+		    unsigned char banned_flags)
 {
 	struct inet6_dev *idev;
 	int err = -EADDRNOTAVAIL;
@@ -1185,7 +1187,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
 
 		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 && !(ifp->flags & banned_flags)) {
 				ipv6_addr_copy(addr, &ifp->addr);
 				err = 0;
 				break;
@@ -1751,6 +1753,7 @@ ok:
 
 			update_lft = create = 1;
 			ifp->cstamp = jiffies;
+			ifp->flags |= IFA_F_OPTIMISTIC;
 			addrconf_dad_start(ifp, RTF_ADDRCONF|RTF_PREFIX_RT);
 		}
 
@@ -1945,7 +1948,11 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
 		ifp->prefered_lft = prefered_lft;
 		ifp->tstamp = jiffies;
 		spin_unlock_bh(&ifp->lock);
-
+		/*
+		 * Note that section 3.1 of RFC 4429 indicates
+		 * That the Optimistic flag should not be set for
+		 * manually configured addresses
+		 */
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
 		addrconf_verify(0);
@@ -2123,7 +2130,8 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr
 {
 	struct inet6_ifaddr * ifp;
 
-	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
+	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, 
+		            IFA_F_PERMANENT|IFA_F_OPTIMISTIC);
 	if (!IS_ERR(ifp)) {
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
@@ -2190,7 +2198,7 @@ ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
 {
 	struct in6_addr lladdr;
 
-	if (!ipv6_get_lladdr(link_dev, &lladdr)) {
+	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
 		addrconf_add_linklocal(idev, &lladdr);
 		return 0;
 	}
@@ -2537,8 +2545,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	struct inet6_dev *idev = ifp->idev;
 	struct net_device *dev = idev->dev;
 
+	if (!idev->cnf.optimistic_dad)
+		ifp->flags &= ~IFA_F_OPTIMISTIC;
+
 	addrconf_join_solict(dev, &ifp->addr);
 
+	/*
+	 * Optimistic nodes need to joing the anycast address
+	 * right away
+	 */
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addrconf_join_anycast(ifp);
+
 	if (ifp->prefix_len != 128 && (ifp->flags&IFA_F_PERMANENT))
 		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 0,
 					flags);
@@ -2553,7 +2571,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
 	    !(ifp->flags&IFA_F_TENTATIVE) ||
 	    ifp->flags & IFA_F_NODAD) {
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -2573,6 +2591,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 		addrconf_dad_stop(ifp);
 		return;
 	}
+
+	/*
+	 * Forwarding devices (routers) should not use
+	 * optimistic addresses
+	 * Nor should interfaces that don't know the 
+	 * Source address for their default gateway
+	 * RFC 4429 Sec 3.3
+	 */
+	if ((ipv6_devconf.forwarding) ||
+	   (ifp->rt == NULL))
+		ifp->flags &= ~IFA_F_OPTIMISTIC;
+
 	addrconf_dad_kick(ifp);
 	spin_unlock_bh(&ifp->lock);
 out:
@@ -2597,7 +2627,7 @@ static void addrconf_dad_timer(unsigned long data)
 		 * DAD was successful
 		 */
 
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -3398,6 +3428,7 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
 #endif
 #endif
 	array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp;
+	array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad;
 }
 
 static inline size_t inet6_if_nlmsg_size(void)
@@ -3918,6 +3949,15 @@ static struct addrconf_sysctl_table
 			.proc_handler	=	&proc_dointvec,
 		},
 		{
+			.ctl_name	=	NET_IPV6_OPTIMISTIC_DAD,
+			.procname       =       "optimistic_dad",
+			.data           =       &ipv6_devconf.optimistic_dad,
+			.maxlen         =       sizeof(int),
+			.mode           =       0644,
+			.proc_handler   =       &proc_dointvec,
+
+		},
+		{
 			.ctl_name	=	0,	/* sentinel */
 		}
 	},
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 882cde4..9c5273c 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
@@ -1789,7 +1789,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 6a9f616..fcd22e3 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -498,7 +498,21 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
         msg->icmph.icmp6_unused = 0;
         msg->icmph.icmp6_router    = router;
         msg->icmph.icmp6_solicited = solicited;
-        msg->icmph.icmp6_override  = override;
+	if (!ifp || !(ifp->flags & IFA_F_OPTIMISTIC))
+		msg->icmph.icmp6_override  = override;
+	else {
+		/*
+		 * We must clear the override flag on all
+		 * neighbor advertisements from source 
+		 * addresses that are OPTIMISTIC - RFC 4429
+		 * section 2.2
+		 */
+		if (override)
+			printk(KERN_WARNING
+				"Disallowing override flag for OPTIMISTIC addr\n");
+		msg->icmph.icmp6_override = 0;
+	}
+
 
         /* Set the target address. */
 	ipv6_addr_copy(&msg->target, solicited_addr);
@@ -542,7 +556,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
 	int send_llinfo;
 
 	if (saddr == NULL) {
-		if (ipv6_get_lladdr(dev, &addr_buf))
+		if (ipv6_get_lladdr(dev, &addr_buf,
+			            (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
 			return;
 		saddr = &addr_buf;
 	}
@@ -622,9 +637,20 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
         struct sk_buff *skb;
         struct icmp6hdr *hdr;
 	__u8 * opt;
+	struct inet6_ifaddr *ifp;
         int len;
 	int err;
 
+	/*
+	 * Check the source address.  If its OPTIMISTIC
+	 * and addr_len is non-zero (implying the sllao option)
+	 * then don't send the RS (RFC 4429, section 2.2)
+	 */
+	ifp = ipv6_get_ifaddr(saddr, dev, 1);
+
+	if ((!ifp) || ((ifp->flags & IFA_F_OPTIMISTIC) && dev->addr_len))
+		return;
+
 	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
 			dev->ifindex);
 
@@ -746,6 +772,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	int dad = ipv6_addr_any(saddr);
 	int inc;
 	int is_router;
+	int type;
 
 	if (ipv6_addr_is_multicast(&msg->target)) {
 		ND_PRINTK2(KERN_WARNING 
@@ -796,28 +823,39 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	inc = ipv6_addr_is_multicast(daddr);
 
 	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
-		if (ifp->flags & IFA_F_TENTATIVE) {
-			/* Address is tentative. If the source
-			   is unspecified address, it is someone
-			   does DAD, otherwise we ignore solicitations
-			   until DAD timer expires.
-			 */
-			if (!dad)
+
+		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
+			if (dad) {
+				if (dev->type == ARPHRD_IEEE802_TR) {
+					unsigned char *sadr = skb->mac.raw;
+					if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
+					    sadr[9] == dev->dev_addr[1] &&
+					    sadr[10] == dev->dev_addr[2] &&
+					    sadr[11] == dev->dev_addr[3] &&
+					    sadr[12] == dev->dev_addr[4] &&
+					    sadr[13] == dev->dev_addr[5]) {
+						/* looped-back to us */
+						goto out;
+					}
+				}
+
+				/*
+				 * We are colliding with another node
+				 * who is doing DAD
+				 * so fail our DAD process
+				 */
+				addrconf_dad_failure(ifp);
 				goto out;
-			if (dev->type == ARPHRD_IEEE802_TR) {
-				unsigned char *sadr = skb->mac.raw;
-				if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
-				    sadr[9] == dev->dev_addr[1] &&
-				    sadr[10] == dev->dev_addr[2] &&
-				    sadr[11] == dev->dev_addr[3] &&
-				    sadr[12] == dev->dev_addr[4] &&
-				    sadr[13] == dev->dev_addr[5]) {
-					/* looped-back to us */
+			} else {
+				/*
+				 * This is not a dad solicitation.
+				 * If we are an optimistic node, 
+				 * we should respond.  
+				 * Otherwise, we should ignore it. 
+				 */
+				if (!(ifp->flags & IFA_F_OPTIMISTIC))
 					goto out;
-				}
 			}
-			addrconf_dad_failure(ifp); 
-			return;
 		}
 
 		idev = ifp->idev;
@@ -1406,7 +1444,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
 
 	dev = skb->dev;
 
-	if (ipv6_get_lladdr(dev, &saddr_buf)) {
+	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
 		ND_PRINTK2(KERN_WARNING
 			   "ICMPv6 Redirect: no link-local address on %s\n",
 			   dev->name);

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-23 20:51       ` Neil Horman
@ 2007-01-25  1:54         ` Sridhar Samudrala
  2007-01-25 13:33           ` Neil Horman
  0 siblings, 1 reply; 71+ messages in thread
From: Sridhar Samudrala @ 2007-01-25  1:54 UTC (permalink / raw)
  To: Neil Horman
  Cc: YOSHIFUJI Hideaki / 吉藤英明,
	davem, kuznet, pekkas, jmorris, kaber, netdev

Sec 2.1 of RFC 4429 says

   Unless noted otherwise, components of the IPv6 protocol stack should
   treat addresses in the Optimistic state equivalently to those in the
   Deprecated state, indicating that the address is available for use
   but should not be used if another suitable address is available.  For
   example, Default Address Selection [RFC3484] uses the address state
   to decide which source address to use for an outgoing packet.
   Implementations should treat an address in state Optimistic as if it
   were in state Deprecated.  If address states are recorded as
   individual flags, this can easily be achieved by also setting
   'Deprecated' when 'Optimistic' is set.

So i think DEPRECATED flag also should be set when we mark an address
as OPTIMISTIC so that we don't use it as source address for new 
connections if another address is available until DAD is completed.

Thanks
Sridhar


On Tue, 2007-01-23 at 15:51 -0500, Neil Horman wrote:
> On Tue, Jan 23, 2007 at 09:18:20AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
> > Hello.
> <snip>
> 
> New patch attached, incorporating Yoshijui and Vlads latest comments.  I didn't
> follow guidance on the ndisc_recv_ns comment, Yoshifuji, since Vlad had already
> suggested an alternate solution in a previous post, but from looking at them
> both, they should be equivalent.
> 
> Thanks & Regards
> Neil
> 
> Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
> 
> 
>  include/linux/if_addr.h |    1
>  include/linux/ipv6.h    |    2 +
>  include/linux/sysctl.h  |    1
>  include/net/addrconf.h  |    4 +-
>  net/ipv6/addrconf.c     |   56 ++++++++++++++++++++++++++++----
>  net/ipv6/mcast.c        |    4 +-
>  net/ipv6/ndisc.c        |   82 +++++++++++++++++++++++++++++++++++-------------
>  7 files changed, 117 insertions(+), 33 deletions(-)
> 
> 
> diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
> index d557e4c..43f3bed 100644
> --- a/include/linux/if_addr.h
> +++ b/include/linux/if_addr.h
> @@ -39,6 +39,7 @@ enum
>  #define IFA_F_TEMPORARY		IFA_F_SECONDARY
> 
>  #define	IFA_F_NODAD		0x02
> +#define IFA_F_OPTIMISTIC	0x04
>  #define	IFA_F_HOMEADDRESS	0x10
>  #define IFA_F_DEPRECATED	0x20
>  #define IFA_F_TENTATIVE		0x40
> diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
> index f824113..5d37abf 100644
> --- a/include/linux/ipv6.h
> +++ b/include/linux/ipv6.h
> @@ -177,6 +177,7 @@ struct ipv6_devconf {
>  #endif
>  #endif
>  	__s32		proxy_ndp;
> +	__s32		optimistic_dad;
>  	void		*sysctl;
>  };
> 
> @@ -205,6 +206,7 @@ enum {
>  	DEVCONF_RTR_PROBE_INTERVAL,
>  	DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN,
>  	DEVCONF_PROXY_NDP,
> +	DEVCONF_OPTIMISTIC_DAD,
>  	DEVCONF_MAX
>  };
> 
> diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
> index 81480e6..972a33a 100644
> --- a/include/linux/sysctl.h
> +++ b/include/linux/sysctl.h
> @@ -570,6 +570,7 @@ enum {
>  	NET_IPV6_RTR_PROBE_INTERVAL=21,
>  	NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22,
>  	NET_IPV6_PROXY_NDP=23,
> +	NET_IPV6_OPTIMISTIC_DAD=24,
>  	__NET_IPV6_MAX
>  };
> 
> diff --git a/include/net/addrconf.h b/include/net/addrconf.h
> index 88df8fc..d248a19 100644
> --- a/include/net/addrconf.h
> +++ b/include/net/addrconf.h
> @@ -73,7 +73,9 @@ extern int			ipv6_get_saddr(struct dst_entry *dst,
>  extern int			ipv6_dev_get_saddr(struct net_device *dev,
>  					       struct in6_addr *daddr,
>  					       struct in6_addr *saddr);
> -extern int			ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
> +extern int			ipv6_get_lladdr(struct net_device *dev,
> +						struct in6_addr *,
> +						unsigned char banned_flags);
>  extern int			ipv6_rcv_saddr_equal(const struct sock *sk,
>  						      const struct sock *sk2);
>  extern void			addrconf_join_solict(struct net_device *dev,
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index 2a7e461..d2b01ec 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -830,7 +830,8 @@ retry:
>  	ift = !max_addresses ||
>  	      ipv6_count_addresses(idev) < max_addresses ?
>  		ipv6_add_addr(idev, &addr, tmp_plen,
> -			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
> +			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK,
> +			      IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
>  	if (!ift || IS_ERR(ift)) {
>  		in6_ifa_put(ifp);
>  		in6_dev_put(idev);
> @@ -1174,7 +1175,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
>  }
> 
> 
> -int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
> +int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
> +		    unsigned char banned_flags)
>  {
>  	struct inet6_dev *idev;
>  	int err = -EADDRNOTAVAIL;
> @@ -1185,7 +1187,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
> 
>  		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 && !(ifp->flags & banned_flags)) {
>  				ipv6_addr_copy(addr, &ifp->addr);
>  				err = 0;
>  				break;
> @@ -1751,6 +1753,7 @@ ok:
> 
>  			update_lft = create = 1;
>  			ifp->cstamp = jiffies;
> +			ifp->flags |= IFA_F_OPTIMISTIC;
>  			addrconf_dad_start(ifp, RTF_ADDRCONF|RTF_PREFIX_RT);
>  		}
> 
> @@ -1945,7 +1948,11 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
>  		ifp->prefered_lft = prefered_lft;
>  		ifp->tstamp = jiffies;
>  		spin_unlock_bh(&ifp->lock);
> -
> +		/*
> +		 * Note that section 3.1 of RFC 4429 indicates
> +		 * That the Optimistic flag should not be set for
> +		 * manually configured addresses
> +		 */
>  		addrconf_dad_start(ifp, 0);
>  		in6_ifa_put(ifp);
>  		addrconf_verify(0);
> @@ -2123,7 +2130,8 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr
>  {
>  	struct inet6_ifaddr * ifp;
> 
> -	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
> +	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK,
> +		            IFA_F_PERMANENT|IFA_F_OPTIMISTIC);
>  	if (!IS_ERR(ifp)) {
>  		addrconf_dad_start(ifp, 0);
>  		in6_ifa_put(ifp);
> @@ -2190,7 +2198,7 @@ ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
>  {
>  	struct in6_addr lladdr;
> 
> -	if (!ipv6_get_lladdr(link_dev, &lladdr)) {
> +	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
>  		addrconf_add_linklocal(idev, &lladdr);
>  		return 0;
>  	}
> @@ -2537,8 +2545,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
>  	struct inet6_dev *idev = ifp->idev;
>  	struct net_device *dev = idev->dev;
> 
> +	if (!idev->cnf.optimistic_dad)
> +		ifp->flags &= ~IFA_F_OPTIMISTIC;
> +
>  	addrconf_join_solict(dev, &ifp->addr);
> 
> +	/*
> +	 * Optimistic nodes need to joing the anycast address
> +	 * right away
> +	 */
> +	if (ifp->flags & IFA_F_OPTIMISTIC)
> +		addrconf_join_anycast(ifp);
> +
>  	if (ifp->prefix_len != 128 && (ifp->flags&IFA_F_PERMANENT))
>  		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 0,
>  					flags);
> @@ -2553,7 +2571,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
>  	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
>  	    !(ifp->flags&IFA_F_TENTATIVE) ||
>  	    ifp->flags & IFA_F_NODAD) {
> -		ifp->flags &= ~IFA_F_TENTATIVE;
> +		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
>  		spin_unlock_bh(&ifp->lock);
>  		read_unlock_bh(&idev->lock);
> 
> @@ -2573,6 +2591,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
>  		addrconf_dad_stop(ifp);
>  		return;
>  	}
> +
> +	/*
> +	 * Forwarding devices (routers) should not use
> +	 * optimistic addresses
> +	 * Nor should interfaces that don't know the
> +	 * Source address for their default gateway
> +	 * RFC 4429 Sec 3.3
> +	 */
> +	if ((ipv6_devconf.forwarding) ||
> +	   (ifp->rt == NULL))
> +		ifp->flags &= ~IFA_F_OPTIMISTIC;
> +
>  	addrconf_dad_kick(ifp);
>  	spin_unlock_bh(&ifp->lock);
>  out:
> @@ -2597,7 +2627,7 @@ static void addrconf_dad_timer(unsigned long data)
>  		 * DAD was successful
>  		 */
> 
> -		ifp->flags &= ~IFA_F_TENTATIVE;
> +		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
>  		spin_unlock_bh(&ifp->lock);
>  		read_unlock_bh(&idev->lock);
> 
> @@ -3398,6 +3428,7 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
>  #endif
>  #endif
>  	array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp;
> +	array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad;
>  }
> 
>  static inline size_t inet6_if_nlmsg_size(void)
> @@ -3918,6 +3949,15 @@ static struct addrconf_sysctl_table
>  			.proc_handler	=	&proc_dointvec,
>  		},
>  		{
> +			.ctl_name	=	NET_IPV6_OPTIMISTIC_DAD,
> +			.procname       =       "optimistic_dad",
> +			.data           =       &ipv6_devconf.optimistic_dad,
> +			.maxlen         =       sizeof(int),
> +			.mode           =       0644,
> +			.proc_handler   =       &proc_dointvec,
> +
> +		},
> +		{
>  			.ctl_name	=	0,	/* sentinel */
>  		}
>  	},
> diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
> index 882cde4..9c5273c 100644
> --- a/net/ipv6/mcast.c
> +++ b/net/ipv6/mcast.c
> @@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
> 
>  	skb_reserve(skb, LL_RESERVED_SPACE(dev));
> 
> -	if (ipv6_get_lladdr(dev, &addr_buf)) {
> +	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
>  		/* <draft-ietf-magma-mld-source-05.txt>:
>  		 * use unspecified address as the source address
>  		 * when a valid link-local address is not available.
> @@ -1789,7 +1789,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
> 
>  	skb_reserve(skb, LL_RESERVED_SPACE(dev));
> 
> -	if (ipv6_get_lladdr(dev, &addr_buf)) {
> +	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
>  		/* <draft-ietf-magma-mld-source-05.txt>:
>  		 * use unspecified address as the source address
>  		 * when a valid link-local address is not available.
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index 6a9f616..fcd22e3 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -498,7 +498,21 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
>          msg->icmph.icmp6_unused = 0;
>          msg->icmph.icmp6_router    = router;
>          msg->icmph.icmp6_solicited = solicited;
> -        msg->icmph.icmp6_override  = override;
> +	if (!ifp || !(ifp->flags & IFA_F_OPTIMISTIC))
> +		msg->icmph.icmp6_override  = override;
> +	else {
> +		/*
> +		 * We must clear the override flag on all
> +		 * neighbor advertisements from source
> +		 * addresses that are OPTIMISTIC - RFC 4429
> +		 * section 2.2
> +		 */
> +		if (override)
> +			printk(KERN_WARNING
> +				"Disallowing override flag for OPTIMISTIC addr\n");
> +		msg->icmph.icmp6_override = 0;
> +	}
> +
> 
>          /* Set the target address. */
>  	ipv6_addr_copy(&msg->target, solicited_addr);
> @@ -542,7 +556,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
>  	int send_llinfo;
> 
>  	if (saddr == NULL) {
> -		if (ipv6_get_lladdr(dev, &addr_buf))
> +		if (ipv6_get_lladdr(dev, &addr_buf,
> +			            (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
>  			return;
>  		saddr = &addr_buf;
>  	}
> @@ -622,9 +637,20 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
>          struct sk_buff *skb;
>          struct icmp6hdr *hdr;
>  	__u8 * opt;
> +	struct inet6_ifaddr *ifp;
>          int len;
>  	int err;
> 
> +	/*
> +	 * Check the source address.  If its OPTIMISTIC
> +	 * and addr_len is non-zero (implying the sllao option)
> +	 * then don't send the RS (RFC 4429, section 2.2)
> +	 */
> +	ifp = ipv6_get_ifaddr(saddr, dev, 1);
> +
> +	if ((!ifp) || ((ifp->flags & IFA_F_OPTIMISTIC) && dev->addr_len))
> +		return;
> +
>  	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
>  			dev->ifindex);
> 
> @@ -746,6 +772,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
>  	int dad = ipv6_addr_any(saddr);
>  	int inc;
>  	int is_router;
> +	int type;
> 
>  	if (ipv6_addr_is_multicast(&msg->target)) {
>  		ND_PRINTK2(KERN_WARNING
> @@ -796,28 +823,39 @@ static void ndisc_recv_ns(struct sk_buff *skb)
>  	inc = ipv6_addr_is_multicast(daddr);
> 
>  	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
> -		if (ifp->flags & IFA_F_TENTATIVE) {
> -			/* Address is tentative. If the source
> -			   is unspecified address, it is someone
> -			   does DAD, otherwise we ignore solicitations
> -			   until DAD timer expires.
> -			 */
> -			if (!dad)
> +
> +		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
> +			if (dad) {
> +				if (dev->type == ARPHRD_IEEE802_TR) {
> +					unsigned char *sadr = skb->mac.raw;
> +					if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
> +					    sadr[9] == dev->dev_addr[1] &&
> +					    sadr[10] == dev->dev_addr[2] &&
> +					    sadr[11] == dev->dev_addr[3] &&
> +					    sadr[12] == dev->dev_addr[4] &&
> +					    sadr[13] == dev->dev_addr[5]) {
> +						/* looped-back to us */
> +						goto out;
> +					}
> +				}
> +
> +				/*
> +				 * We are colliding with another node
> +				 * who is doing DAD
> +				 * so fail our DAD process
> +				 */
> +				addrconf_dad_failure(ifp);
>  				goto out;
> -			if (dev->type == ARPHRD_IEEE802_TR) {
> -				unsigned char *sadr = skb->mac.raw;
> -				if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
> -				    sadr[9] == dev->dev_addr[1] &&
> -				    sadr[10] == dev->dev_addr[2] &&
> -				    sadr[11] == dev->dev_addr[3] &&
> -				    sadr[12] == dev->dev_addr[4] &&
> -				    sadr[13] == dev->dev_addr[5]) {
> -					/* looped-back to us */
> +			} else {
> +				/*
> +				 * This is not a dad solicitation.
> +				 * If we are an optimistic node,
> +				 * we should respond.
> +				 * Otherwise, we should ignore it.
> +				 */
> +				if (!(ifp->flags & IFA_F_OPTIMISTIC))
>  					goto out;
> -				}
>  			}
> -			addrconf_dad_failure(ifp);
> -			return;
>  		}
> 
>  		idev = ifp->idev;
> @@ -1406,7 +1444,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
> 
>  	dev = skb->dev;
> 
> -	if (ipv6_get_lladdr(dev, &saddr_buf)) {
> +	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
>  		ND_PRINTK2(KERN_WARNING
>  			   "ICMPv6 Redirect: no link-local address on %s\n",
>  			   dev->name);
> -
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-25  1:54         ` Sridhar Samudrala
@ 2007-01-25 13:33           ` Neil Horman
  2007-01-25 17:16             ` Vlad Yasevich
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-01-25 13:33 UTC (permalink / raw)
  To: Sridhar Samudrala
  Cc: YOSHIFUJI Hideaki / 吉藤英明,
	davem, kuznet, pekkas, jmorris, kaber, netdev

On Wed, Jan 24, 2007 at 05:54:47PM -0800, Sridhar Samudrala wrote:
> Sec 2.1 of RFC 4429 says
> 
>    Unless noted otherwise, components of the IPv6 protocol stack should
>    treat addresses in the Optimistic state equivalently to those in the
>    Deprecated state, indicating that the address is available for use
>    but should not be used if another suitable address is available.  For
>    example, Default Address Selection [RFC3484] uses the address state
>    to decide which source address to use for an outgoing packet.
>    Implementations should treat an address in state Optimistic as if it
>    were in state Deprecated.  If address states are recorded as
>    individual flags, this can easily be achieved by also setting
>    'Deprecated' when 'Optimistic' is set.
> 
> So i think DEPRECATED flag also should be set when we mark an address
> as OPTIMISTIC so that we don't use it as source address for new 
> connections if another address is available until DAD is completed.
> 
> Thanks
> Sridhar
> 

Oh, good catch.  Thank you Sri.  However, I'm worried about the next paragraph:

It is important to note that the address lifetime rules of [RFC2462]
   still apply, and so an address may be Deprecated as well as
   Optimistic.  When DAD completes without incident, the address becomes
   either a Preferred or a Deprecated address, as per RFC 2462

Given that, it seems to me that addresses which are flagged as Deprecated may
enter and exit that state independently of the DAD process, which I think gives
rise to the possibility of a race.  I.e. if an address becomes deprecated right
before DAD completes, and then addrconf_dad_complete clears the IFA_F_DEPRECATED
flag, that seems wrong.  Instead I think it would be better if we tested for the
OPTIMISTIC flag in ipv6_dev_get_saddr in parallel with the DEPRECATED flag.  I
may be wrong about this, but I'm going to err on the side of safety.  If you can
ensure that this race is not possible.  Please let me know, and I'll happily
just set the flag.  I'll repost a new patch soon.

Thanks & Regards
Neil

> 
> On Tue, 2007-01-23 at 15:51 -0500, Neil Horman wrote:
> > On Tue, Jan 23, 2007 at 09:18:20AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
> > > Hello.
> > <snip>
> > 
> > New patch attached, incorporating Yoshijui and Vlads latest comments.  I didn't
> > follow guidance on the ndisc_recv_ns comment, Yoshifuji, since Vlad had already
> > suggested an alternate solution in a previous post, but from looking at them
> > both, they should be equivalent.
> > 
> > Thanks & Regards
> > Neil
> > 
> > Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
> > 
> > 
> >  include/linux/if_addr.h |    1
> >  include/linux/ipv6.h    |    2 +
> >  include/linux/sysctl.h  |    1
> >  include/net/addrconf.h  |    4 +-
> >  net/ipv6/addrconf.c     |   56 ++++++++++++++++++++++++++++----
> >  net/ipv6/mcast.c        |    4 +-
> >  net/ipv6/ndisc.c        |   82 +++++++++++++++++++++++++++++++++++-------------
> >  7 files changed, 117 insertions(+), 33 deletions(-)
> > 
> > 
> > diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
> > index d557e4c..43f3bed 100644
> > --- a/include/linux/if_addr.h
> > +++ b/include/linux/if_addr.h
> > @@ -39,6 +39,7 @@ enum
> >  #define IFA_F_TEMPORARY		IFA_F_SECONDARY
> > 
> >  #define	IFA_F_NODAD		0x02
> > +#define IFA_F_OPTIMISTIC	0x04
> >  #define	IFA_F_HOMEADDRESS	0x10
> >  #define IFA_F_DEPRECATED	0x20
> >  #define IFA_F_TENTATIVE		0x40
> > diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
> > index f824113..5d37abf 100644
> > --- a/include/linux/ipv6.h
> > +++ b/include/linux/ipv6.h
> > @@ -177,6 +177,7 @@ struct ipv6_devconf {
> >  #endif
> >  #endif
> >  	__s32		proxy_ndp;
> > +	__s32		optimistic_dad;
> >  	void		*sysctl;
> >  };
> > 
> > @@ -205,6 +206,7 @@ enum {
> >  	DEVCONF_RTR_PROBE_INTERVAL,
> >  	DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN,
> >  	DEVCONF_PROXY_NDP,
> > +	DEVCONF_OPTIMISTIC_DAD,
> >  	DEVCONF_MAX
> >  };
> > 
> > diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
> > index 81480e6..972a33a 100644
> > --- a/include/linux/sysctl.h
> > +++ b/include/linux/sysctl.h
> > @@ -570,6 +570,7 @@ enum {
> >  	NET_IPV6_RTR_PROBE_INTERVAL=21,
> >  	NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22,
> >  	NET_IPV6_PROXY_NDP=23,
> > +	NET_IPV6_OPTIMISTIC_DAD=24,
> >  	__NET_IPV6_MAX
> >  };
> > 
> > diff --git a/include/net/addrconf.h b/include/net/addrconf.h
> > index 88df8fc..d248a19 100644
> > --- a/include/net/addrconf.h
> > +++ b/include/net/addrconf.h
> > @@ -73,7 +73,9 @@ extern int			ipv6_get_saddr(struct dst_entry *dst,
> >  extern int			ipv6_dev_get_saddr(struct net_device *dev,
> >  					       struct in6_addr *daddr,
> >  					       struct in6_addr *saddr);
> > -extern int			ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
> > +extern int			ipv6_get_lladdr(struct net_device *dev,
> > +						struct in6_addr *,
> > +						unsigned char banned_flags);
> >  extern int			ipv6_rcv_saddr_equal(const struct sock *sk,
> >  						      const struct sock *sk2);
> >  extern void			addrconf_join_solict(struct net_device *dev,
> > diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> > index 2a7e461..d2b01ec 100644
> > --- a/net/ipv6/addrconf.c
> > +++ b/net/ipv6/addrconf.c
> > @@ -830,7 +830,8 @@ retry:
> >  	ift = !max_addresses ||
> >  	      ipv6_count_addresses(idev) < max_addresses ?
> >  		ipv6_add_addr(idev, &addr, tmp_plen,
> > -			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
> > +			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK,
> > +			      IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
> >  	if (!ift || IS_ERR(ift)) {
> >  		in6_ifa_put(ifp);
> >  		in6_dev_put(idev);
> > @@ -1174,7 +1175,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
> >  }
> > 
> > 
> > -int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
> > +int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
> > +		    unsigned char banned_flags)
> >  {
> >  	struct inet6_dev *idev;
> >  	int err = -EADDRNOTAVAIL;
> > @@ -1185,7 +1187,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
> > 
> >  		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 && !(ifp->flags & banned_flags)) {
> >  				ipv6_addr_copy(addr, &ifp->addr);
> >  				err = 0;
> >  				break;
> > @@ -1751,6 +1753,7 @@ ok:
> > 
> >  			update_lft = create = 1;
> >  			ifp->cstamp = jiffies;
> > +			ifp->flags |= IFA_F_OPTIMISTIC;
> >  			addrconf_dad_start(ifp, RTF_ADDRCONF|RTF_PREFIX_RT);
> >  		}
> > 
> > @@ -1945,7 +1948,11 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
> >  		ifp->prefered_lft = prefered_lft;
> >  		ifp->tstamp = jiffies;
> >  		spin_unlock_bh(&ifp->lock);
> > -
> > +		/*
> > +		 * Note that section 3.1 of RFC 4429 indicates
> > +		 * That the Optimistic flag should not be set for
> > +		 * manually configured addresses
> > +		 */
> >  		addrconf_dad_start(ifp, 0);
> >  		in6_ifa_put(ifp);
> >  		addrconf_verify(0);
> > @@ -2123,7 +2130,8 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr
> >  {
> >  	struct inet6_ifaddr * ifp;
> > 
> > -	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
> > +	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK,
> > +		            IFA_F_PERMANENT|IFA_F_OPTIMISTIC);
> >  	if (!IS_ERR(ifp)) {
> >  		addrconf_dad_start(ifp, 0);
> >  		in6_ifa_put(ifp);
> > @@ -2190,7 +2198,7 @@ ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
> >  {
> >  	struct in6_addr lladdr;
> > 
> > -	if (!ipv6_get_lladdr(link_dev, &lladdr)) {
> > +	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
> >  		addrconf_add_linklocal(idev, &lladdr);
> >  		return 0;
> >  	}
> > @@ -2537,8 +2545,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
> >  	struct inet6_dev *idev = ifp->idev;
> >  	struct net_device *dev = idev->dev;
> > 
> > +	if (!idev->cnf.optimistic_dad)
> > +		ifp->flags &= ~IFA_F_OPTIMISTIC;
> > +
> >  	addrconf_join_solict(dev, &ifp->addr);
> > 
> > +	/*
> > +	 * Optimistic nodes need to joing the anycast address
> > +	 * right away
> > +	 */
> > +	if (ifp->flags & IFA_F_OPTIMISTIC)
> > +		addrconf_join_anycast(ifp);
> > +
> >  	if (ifp->prefix_len != 128 && (ifp->flags&IFA_F_PERMANENT))
> >  		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 0,
> >  					flags);
> > @@ -2553,7 +2571,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
> >  	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
> >  	    !(ifp->flags&IFA_F_TENTATIVE) ||
> >  	    ifp->flags & IFA_F_NODAD) {
> > -		ifp->flags &= ~IFA_F_TENTATIVE;
> > +		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
> >  		spin_unlock_bh(&ifp->lock);
> >  		read_unlock_bh(&idev->lock);
> > 
> > @@ -2573,6 +2591,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
> >  		addrconf_dad_stop(ifp);
> >  		return;
> >  	}
> > +
> > +	/*
> > +	 * Forwarding devices (routers) should not use
> > +	 * optimistic addresses
> > +	 * Nor should interfaces that don't know the
> > +	 * Source address for their default gateway
> > +	 * RFC 4429 Sec 3.3
> > +	 */
> > +	if ((ipv6_devconf.forwarding) ||
> > +	   (ifp->rt == NULL))
> > +		ifp->flags &= ~IFA_F_OPTIMISTIC;
> > +
> >  	addrconf_dad_kick(ifp);
> >  	spin_unlock_bh(&ifp->lock);
> >  out:
> > @@ -2597,7 +2627,7 @@ static void addrconf_dad_timer(unsigned long data)
> >  		 * DAD was successful
> >  		 */
> > 
> > -		ifp->flags &= ~IFA_F_TENTATIVE;
> > +		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
> >  		spin_unlock_bh(&ifp->lock);
> >  		read_unlock_bh(&idev->lock);
> > 
> > @@ -3398,6 +3428,7 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
> >  #endif
> >  #endif
> >  	array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp;
> > +	array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad;
> >  }
> > 
> >  static inline size_t inet6_if_nlmsg_size(void)
> > @@ -3918,6 +3949,15 @@ static struct addrconf_sysctl_table
> >  			.proc_handler	=	&proc_dointvec,
> >  		},
> >  		{
> > +			.ctl_name	=	NET_IPV6_OPTIMISTIC_DAD,
> > +			.procname       =       "optimistic_dad",
> > +			.data           =       &ipv6_devconf.optimistic_dad,
> > +			.maxlen         =       sizeof(int),
> > +			.mode           =       0644,
> > +			.proc_handler   =       &proc_dointvec,
> > +
> > +		},
> > +		{
> >  			.ctl_name	=	0,	/* sentinel */
> >  		}
> >  	},
> > diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
> > index 882cde4..9c5273c 100644
> > --- a/net/ipv6/mcast.c
> > +++ b/net/ipv6/mcast.c
> > @@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
> > 
> >  	skb_reserve(skb, LL_RESERVED_SPACE(dev));
> > 
> > -	if (ipv6_get_lladdr(dev, &addr_buf)) {
> > +	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
> >  		/* <draft-ietf-magma-mld-source-05.txt>:
> >  		 * use unspecified address as the source address
> >  		 * when a valid link-local address is not available.
> > @@ -1789,7 +1789,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
> > 
> >  	skb_reserve(skb, LL_RESERVED_SPACE(dev));
> > 
> > -	if (ipv6_get_lladdr(dev, &addr_buf)) {
> > +	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
> >  		/* <draft-ietf-magma-mld-source-05.txt>:
> >  		 * use unspecified address as the source address
> >  		 * when a valid link-local address is not available.
> > diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> > index 6a9f616..fcd22e3 100644
> > --- a/net/ipv6/ndisc.c
> > +++ b/net/ipv6/ndisc.c
> > @@ -498,7 +498,21 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
> >          msg->icmph.icmp6_unused = 0;
> >          msg->icmph.icmp6_router    = router;
> >          msg->icmph.icmp6_solicited = solicited;
> > -        msg->icmph.icmp6_override  = override;
> > +	if (!ifp || !(ifp->flags & IFA_F_OPTIMISTIC))
> > +		msg->icmph.icmp6_override  = override;
> > +	else {
> > +		/*
> > +		 * We must clear the override flag on all
> > +		 * neighbor advertisements from source
> > +		 * addresses that are OPTIMISTIC - RFC 4429
> > +		 * section 2.2
> > +		 */
> > +		if (override)
> > +			printk(KERN_WARNING
> > +				"Disallowing override flag for OPTIMISTIC addr\n");
> > +		msg->icmph.icmp6_override = 0;
> > +	}
> > +
> > 
> >          /* Set the target address. */
> >  	ipv6_addr_copy(&msg->target, solicited_addr);
> > @@ -542,7 +556,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
> >  	int send_llinfo;
> > 
> >  	if (saddr == NULL) {
> > -		if (ipv6_get_lladdr(dev, &addr_buf))
> > +		if (ipv6_get_lladdr(dev, &addr_buf,
> > +			            (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
> >  			return;
> >  		saddr = &addr_buf;
> >  	}
> > @@ -622,9 +637,20 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
> >          struct sk_buff *skb;
> >          struct icmp6hdr *hdr;
> >  	__u8 * opt;
> > +	struct inet6_ifaddr *ifp;
> >          int len;
> >  	int err;
> > 
> > +	/*
> > +	 * Check the source address.  If its OPTIMISTIC
> > +	 * and addr_len is non-zero (implying the sllao option)
> > +	 * then don't send the RS (RFC 4429, section 2.2)
> > +	 */
> > +	ifp = ipv6_get_ifaddr(saddr, dev, 1);
> > +
> > +	if ((!ifp) || ((ifp->flags & IFA_F_OPTIMISTIC) && dev->addr_len))
> > +		return;
> > +
> >  	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
> >  			dev->ifindex);
> > 
> > @@ -746,6 +772,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
> >  	int dad = ipv6_addr_any(saddr);
> >  	int inc;
> >  	int is_router;
> > +	int type;
> > 
> >  	if (ipv6_addr_is_multicast(&msg->target)) {
> >  		ND_PRINTK2(KERN_WARNING
> > @@ -796,28 +823,39 @@ static void ndisc_recv_ns(struct sk_buff *skb)
> >  	inc = ipv6_addr_is_multicast(daddr);
> > 
> >  	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
> > -		if (ifp->flags & IFA_F_TENTATIVE) {
> > -			/* Address is tentative. If the source
> > -			   is unspecified address, it is someone
> > -			   does DAD, otherwise we ignore solicitations
> > -			   until DAD timer expires.
> > -			 */
> > -			if (!dad)
> > +
> > +		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
> > +			if (dad) {
> > +				if (dev->type == ARPHRD_IEEE802_TR) {
> > +					unsigned char *sadr = skb->mac.raw;
> > +					if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
> > +					    sadr[9] == dev->dev_addr[1] &&
> > +					    sadr[10] == dev->dev_addr[2] &&
> > +					    sadr[11] == dev->dev_addr[3] &&
> > +					    sadr[12] == dev->dev_addr[4] &&
> > +					    sadr[13] == dev->dev_addr[5]) {
> > +						/* looped-back to us */
> > +						goto out;
> > +					}
> > +				}
> > +
> > +				/*
> > +				 * We are colliding with another node
> > +				 * who is doing DAD
> > +				 * so fail our DAD process
> > +				 */
> > +				addrconf_dad_failure(ifp);
> >  				goto out;
> > -			if (dev->type == ARPHRD_IEEE802_TR) {
> > -				unsigned char *sadr = skb->mac.raw;
> > -				if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
> > -				    sadr[9] == dev->dev_addr[1] &&
> > -				    sadr[10] == dev->dev_addr[2] &&
> > -				    sadr[11] == dev->dev_addr[3] &&
> > -				    sadr[12] == dev->dev_addr[4] &&
> > -				    sadr[13] == dev->dev_addr[5]) {
> > -					/* looped-back to us */
> > +			} else {
> > +				/*
> > +				 * This is not a dad solicitation.
> > +				 * If we are an optimistic node,
> > +				 * we should respond.
> > +				 * Otherwise, we should ignore it.
> > +				 */
> > +				if (!(ifp->flags & IFA_F_OPTIMISTIC))
> >  					goto out;
> > -				}
> >  			}
> > -			addrconf_dad_failure(ifp);
> > -			return;
> >  		}
> > 
> >  		idev = ifp->idev;
> > @@ -1406,7 +1444,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
> > 
> >  	dev = skb->dev;
> > 
> > -	if (ipv6_get_lladdr(dev, &saddr_buf)) {
> > +	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
> >  		ND_PRINTK2(KERN_WARNING
> >  			   "ICMPv6 Redirect: no link-local address on %s\n",
> >  			   dev->name);
> > -
> > To unsubscribe from this list: send the line "unsubscribe netdev" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-25 13:33           ` Neil Horman
@ 2007-01-25 17:16             ` Vlad Yasevich
  2007-01-25 19:45               ` Neil Horman
  0 siblings, 1 reply; 71+ messages in thread
From: Vlad Yasevich @ 2007-01-25 17:16 UTC (permalink / raw)
  To: Neil Horman
  Cc: Sridhar Samudrala,
	YOSHIFUJI Hideaki / 吉藤英明,
	davem, kuznet, pekkas, jmorris, kaber, netdev

Neil Horman wrote:
> On Wed, Jan 24, 2007 at 05:54:47PM -0800, Sridhar Samudrala wrote:
>> Sec 2.1 of RFC 4429 says
>>
>>    Unless noted otherwise, components of the IPv6 protocol stack should
>>    treat addresses in the Optimistic state equivalently to those in the
>>    Deprecated state, indicating that the address is available for use
>>    but should not be used if another suitable address is available.  For
>>    example, Default Address Selection [RFC3484] uses the address state
>>    to decide which source address to use for an outgoing packet.
>>    Implementations should treat an address in state Optimistic as if it
>>    were in state Deprecated.  If address states are recorded as
>>    individual flags, this can easily be achieved by also setting
>>    'Deprecated' when 'Optimistic' is set.
>>
>> So i think DEPRECATED flag also should be set when we mark an address
>> as OPTIMISTIC so that we don't use it as source address for new 
>> connections if another address is available until DAD is completed.
>>
>> Thanks
>> Sridhar
>>
> 
> Oh, good catch.  Thank you Sri.  However, I'm worried about the next paragraph:
> 
> It is important to note that the address lifetime rules of [RFC2462]
>    still apply, and so an address may be Deprecated as well as
>    Optimistic.  When DAD completes without incident, the address becomes
>    either a Preferred or a Deprecated address, as per RFC 2462
> 
> Given that, it seems to me that addresses which are flagged as Deprecated may
> enter and exit that state independently of the DAD process, which I think gives
> rise to the possibility of a race.  I.e. if an address becomes deprecated right
> before DAD completes, and then addrconf_dad_complete clears the IFA_F_DEPRECATED
> flag, that seems wrong.  Instead I think it would be better if we tested for the
> OPTIMISTIC flag in ipv6_dev_get_saddr in parallel with the DEPRECATED flag.  I
> may be wrong about this, but I'm going to err on the side of safety.  If you can
> ensure that this race is not possible.  Please let me know, and I'll happily
> just set the flag.  I'll repost a new patch soon.

I tend to agree with Neil here.  Marking optimistic addresses as deprecated doesn't
buy as much since the address can transition in and out of deprecated state regardless
of DAD.

However, there is a problem with the current implementation in that OPTIMISTIC address
will never be chosen as source because it's always TENTATIVE and OPTIMISTIC at the
same time.  What needs to happen is for ipv6_dev_get_saddr() to not ignore OPTIMISTIC
addresses and treat them same as DEPRECATED.

-vlad


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-25 17:16             ` Vlad Yasevich
@ 2007-01-25 19:45               ` Neil Horman
  2007-01-25 20:18                 ` Vlad Yasevich
  2007-01-26  0:13                 ` YOSHIFUJI Hideaki / 吉藤英明
  0 siblings, 2 replies; 71+ messages in thread
From: Neil Horman @ 2007-01-25 19:45 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: Sridhar Samudrala,
	YOSHIFUJI Hideaki / 吉藤英明,
	davem, kuznet, pekkas, jmorris, kaber, netdev

On Thu, Jan 25, 2007 at 12:16:59PM -0500, Vlad Yasevich wrote:
<snip> 
> I tend to agree with Neil here.  Marking optimistic addresses as deprecated doesn't
> buy as much since the address can transition in and out of deprecated state regardless
> of DAD.
> 
> However, there is a problem with the current implementation in that OPTIMISTIC address
> will never be chosen as source because it's always TENTATIVE and OPTIMISTIC at the
> same time.  What needs to happen is for ipv6_dev_get_saddr() to not ignore OPTIMISTIC
> addresses and treat them same as DEPRECATED.
> 
> -vlad


Heres an updated patch.  Same as the previous patch but it adds three
modifications to ipv6_dev_get_saddr, which do the following:

a) Adds logic to not remove addresses that are both tentative and optimistic
from the set of considered addresses

b) Treats optimistic addresses and deptrecated address in the same fashion by
checking for both flags appropriately during source address selection.

Thoughts welcome.

Thanks & Regards
Neil

Signed-off-by: Neil Horman <nhorman@tuxdriver.com


 include/linux/if_addr.h |    1 
 include/linux/ipv6.h    |    2 +
 include/linux/sysctl.h  |    1 
 include/net/addrconf.h  |    4 +-
 net/ipv6/addrconf.c     |   69 ++++++++++++++++++++++++++++++++--------
 net/ipv6/mcast.c        |    4 +-
 net/ipv6/ndisc.c        |   82 +++++++++++++++++++++++++++++++++++-------------
 7 files changed, 125 insertions(+), 38 deletions(-)



diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
index d557e4c..43f3bed 100644
--- a/include/linux/if_addr.h
+++ b/include/linux/if_addr.h
@@ -39,6 +39,7 @@ enum
 #define IFA_F_TEMPORARY		IFA_F_SECONDARY
 
 #define	IFA_F_NODAD		0x02
+#define IFA_F_OPTIMISTIC	0x04
 #define	IFA_F_HOMEADDRESS	0x10
 #define IFA_F_DEPRECATED	0x20
 #define IFA_F_TENTATIVE		0x40
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index f824113..5d37abf 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -177,6 +177,7 @@ struct ipv6_devconf {
 #endif
 #endif
 	__s32		proxy_ndp;
+	__s32		optimistic_dad;
 	void		*sysctl;
 };
 
@@ -205,6 +206,7 @@ enum {
 	DEVCONF_RTR_PROBE_INTERVAL,
 	DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN,
 	DEVCONF_PROXY_NDP,
+	DEVCONF_OPTIMISTIC_DAD,
 	DEVCONF_MAX
 };
 
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 81480e6..972a33a 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -570,6 +570,7 @@ enum {
 	NET_IPV6_RTR_PROBE_INTERVAL=21,
 	NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22,
 	NET_IPV6_PROXY_NDP=23,
+	NET_IPV6_OPTIMISTIC_DAD=24,
 	__NET_IPV6_MAX
 };
 
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 88df8fc..d248a19 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -73,7 +73,9 @@ extern int			ipv6_get_saddr(struct dst_entry *dst,
 extern int			ipv6_dev_get_saddr(struct net_device *dev, 
 					       struct in6_addr *daddr,
 					       struct in6_addr *saddr);
-extern int			ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
+extern int			ipv6_get_lladdr(struct net_device *dev, 
+						struct in6_addr *,
+						unsigned char banned_flags);
 extern int			ipv6_rcv_saddr_equal(const struct sock *sk, 
 						      const struct sock *sk2);
 extern void			addrconf_join_solict(struct net_device *dev,
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 2a7e461..46f91ee 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -830,7 +830,8 @@ retry:
 	ift = !max_addresses ||
 	      ipv6_count_addresses(idev) < max_addresses ? 
 		ipv6_add_addr(idev, &addr, tmp_plen,
-			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
+			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
+			      IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
 	if (!ift || IS_ERR(ift)) {
 		in6_ifa_put(ifp);
 		in6_dev_put(idev);
@@ -962,13 +963,14 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
 			 * - Tentative Address (RFC2462 section 5.4)
 			 *  - A tentative address is not considered
 			 *    "assigned to an interface" in the traditional
-			 *    sense.
+			 *    sense, unless it is also flagged as optimistic.
 			 * - Candidate Source Address (section 4)
 			 *  - In any case, anycast addresses, multicast
 			 *    addresses, and the unspecified address MUST
 			 *    NOT be included in a candidate set.
 			 */
-			if (ifa->flags & IFA_F_TENTATIVE)
+			if ((ifa->flags & IFA_F_TENTATIVE) &&
+			    (!(ifa->flags & IFA_F_OPTIMISTIC)))
 				continue;
 			if (unlikely(score.addr_type == IPV6_ADDR_ANY ||
 				     score.addr_type & IPV6_ADDR_MULTICAST)) {
@@ -1027,15 +1029,17 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
 				}
 			}
 
-			/* Rule 3: Avoid deprecated address */
+			/* Rule 3: Avoid deprecated and optimistic address */
 			if (hiscore.rule < 3) {
 				if (ipv6_saddr_preferred(hiscore.addr_type) ||
-				    !(ifa_result->flags & IFA_F_DEPRECATED))
+				    ((!(ifa_result->flags & IFA_F_DEPRECATED)) && 
+				    (!(ifa_result->flags & IFA_F_OPTIMISTIC))))
 					hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				hiscore.rule++;
 			}
 			if (ipv6_saddr_preferred(score.addr_type) ||
-			    !(ifa->flags & IFA_F_DEPRECATED)) {
+			    ((!(ifa->flags & IFA_F_DEPRECATED)) &&
+			    (!(ifa_result->flags & IFA_F_OPTIMISTIC)))) {
 				score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
 					score.rule = 3;
@@ -1174,7 +1178,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
 }
 
 
-int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
+int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, 
+		    unsigned char banned_flags)
 {
 	struct inet6_dev *idev;
 	int err = -EADDRNOTAVAIL;
@@ -1185,7 +1190,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
 
 		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 && !(ifp->flags & banned_flags)) {
 				ipv6_addr_copy(addr, &ifp->addr);
 				err = 0;
 				break;
@@ -1751,6 +1756,7 @@ ok:
 
 			update_lft = create = 1;
 			ifp->cstamp = jiffies;
+			ifp->flags |= IFA_F_OPTIMISTIC;
 			addrconf_dad_start(ifp, RTF_ADDRCONF|RTF_PREFIX_RT);
 		}
 
@@ -1945,7 +1951,11 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
 		ifp->prefered_lft = prefered_lft;
 		ifp->tstamp = jiffies;
 		spin_unlock_bh(&ifp->lock);
-
+		/*
+		 * Note that section 3.1 of RFC 4429 indicates
+		 * That the Optimistic flag should not be set for
+		 * manually configured addresses
+		 */
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
 		addrconf_verify(0);
@@ -2123,7 +2133,8 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr
 {
 	struct inet6_ifaddr * ifp;
 
-	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
+	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, 
+		            IFA_F_PERMANENT|IFA_F_OPTIMISTIC);
 	if (!IS_ERR(ifp)) {
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
@@ -2190,7 +2201,7 @@ ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
 {
 	struct in6_addr lladdr;
 
-	if (!ipv6_get_lladdr(link_dev, &lladdr)) {
+	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
 		addrconf_add_linklocal(idev, &lladdr);
 		return 0;
 	}
@@ -2537,8 +2548,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	struct inet6_dev *idev = ifp->idev;
 	struct net_device *dev = idev->dev;
 
+	if (!idev->cnf.optimistic_dad)
+		ifp->flags &= ~IFA_F_OPTIMISTIC;
+
 	addrconf_join_solict(dev, &ifp->addr);
 
+	/*
+	 * Optimistic nodes need to joing the anycast address
+	 * right away
+	 */
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addrconf_join_anycast(ifp);
+
 	if (ifp->prefix_len != 128 && (ifp->flags&IFA_F_PERMANENT))
 		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 0,
 					flags);
@@ -2553,7 +2574,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
 	    !(ifp->flags&IFA_F_TENTATIVE) ||
 	    ifp->flags & IFA_F_NODAD) {
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -2573,6 +2594,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 		addrconf_dad_stop(ifp);
 		return;
 	}
+
+	/*
+	 * Forwarding devices (routers) should not use
+	 * optimistic addresses
+	 * Nor should interfaces that don't know the 
+	 * Source address for their default gateway
+	 * RFC 4429 Sec 3.3
+	 */
+	if ((ipv6_devconf.forwarding) ||
+	   (ifp->rt == NULL))
+		ifp->flags &= ~IFA_F_OPTIMISTIC;
+
 	addrconf_dad_kick(ifp);
 	spin_unlock_bh(&ifp->lock);
 out:
@@ -2597,7 +2630,7 @@ static void addrconf_dad_timer(unsigned long data)
 		 * DAD was successful
 		 */
 
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -3398,6 +3431,7 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
 #endif
 #endif
 	array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp;
+	array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad;
 }
 
 static inline size_t inet6_if_nlmsg_size(void)
@@ -3918,6 +3952,15 @@ static struct addrconf_sysctl_table
 			.proc_handler	=	&proc_dointvec,
 		},
 		{
+			.ctl_name	=	NET_IPV6_OPTIMISTIC_DAD,
+			.procname       =       "optimistic_dad",
+			.data           =       &ipv6_devconf.optimistic_dad,
+			.maxlen         =       sizeof(int),
+			.mode           =       0644,
+			.proc_handler   =       &proc_dointvec,
+
+		},
+		{
 			.ctl_name	=	0,	/* sentinel */
 		}
 	},
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 882cde4..9c5273c 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
@@ -1789,7 +1789,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 6a9f616..fcd22e3 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -498,7 +498,21 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
         msg->icmph.icmp6_unused = 0;
         msg->icmph.icmp6_router    = router;
         msg->icmph.icmp6_solicited = solicited;
-        msg->icmph.icmp6_override  = override;
+	if (!ifp || !(ifp->flags & IFA_F_OPTIMISTIC))
+		msg->icmph.icmp6_override  = override;
+	else {
+		/*
+		 * We must clear the override flag on all
+		 * neighbor advertisements from source 
+		 * addresses that are OPTIMISTIC - RFC 4429
+		 * section 2.2
+		 */
+		if (override)
+			printk(KERN_WARNING
+				"Disallowing override flag for OPTIMISTIC addr\n");
+		msg->icmph.icmp6_override = 0;
+	}
+
 
         /* Set the target address. */
 	ipv6_addr_copy(&msg->target, solicited_addr);
@@ -542,7 +556,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
 	int send_llinfo;
 
 	if (saddr == NULL) {
-		if (ipv6_get_lladdr(dev, &addr_buf))
+		if (ipv6_get_lladdr(dev, &addr_buf,
+			            (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
 			return;
 		saddr = &addr_buf;
 	}
@@ -622,9 +637,20 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
         struct sk_buff *skb;
         struct icmp6hdr *hdr;
 	__u8 * opt;
+	struct inet6_ifaddr *ifp;
         int len;
 	int err;
 
+	/*
+	 * Check the source address.  If its OPTIMISTIC
+	 * and addr_len is non-zero (implying the sllao option)
+	 * then don't send the RS (RFC 4429, section 2.2)
+	 */
+	ifp = ipv6_get_ifaddr(saddr, dev, 1);
+
+	if ((!ifp) || ((ifp->flags & IFA_F_OPTIMISTIC) && dev->addr_len))
+		return;
+
 	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
 			dev->ifindex);
 
@@ -746,6 +772,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	int dad = ipv6_addr_any(saddr);
 	int inc;
 	int is_router;
+	int type;
 
 	if (ipv6_addr_is_multicast(&msg->target)) {
 		ND_PRINTK2(KERN_WARNING 
@@ -796,28 +823,39 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	inc = ipv6_addr_is_multicast(daddr);
 
 	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
-		if (ifp->flags & IFA_F_TENTATIVE) {
-			/* Address is tentative. If the source
-			   is unspecified address, it is someone
-			   does DAD, otherwise we ignore solicitations
-			   until DAD timer expires.
-			 */
-			if (!dad)
+
+		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
+			if (dad) {
+				if (dev->type == ARPHRD_IEEE802_TR) {
+					unsigned char *sadr = skb->mac.raw;
+					if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
+					    sadr[9] == dev->dev_addr[1] &&
+					    sadr[10] == dev->dev_addr[2] &&
+					    sadr[11] == dev->dev_addr[3] &&
+					    sadr[12] == dev->dev_addr[4] &&
+					    sadr[13] == dev->dev_addr[5]) {
+						/* looped-back to us */
+						goto out;
+					}
+				}
+
+				/*
+				 * We are colliding with another node
+				 * who is doing DAD
+				 * so fail our DAD process
+				 */
+				addrconf_dad_failure(ifp);
 				goto out;
-			if (dev->type == ARPHRD_IEEE802_TR) {
-				unsigned char *sadr = skb->mac.raw;
-				if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
-				    sadr[9] == dev->dev_addr[1] &&
-				    sadr[10] == dev->dev_addr[2] &&
-				    sadr[11] == dev->dev_addr[3] &&
-				    sadr[12] == dev->dev_addr[4] &&
-				    sadr[13] == dev->dev_addr[5]) {
-					/* looped-back to us */
+			} else {
+				/*
+				 * This is not a dad solicitation.
+				 * If we are an optimistic node, 
+				 * we should respond.  
+				 * Otherwise, we should ignore it. 
+				 */
+				if (!(ifp->flags & IFA_F_OPTIMISTIC))
 					goto out;
-				}
 			}
-			addrconf_dad_failure(ifp); 
-			return;
 		}
 
 		idev = ifp->idev;
@@ -1406,7 +1444,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
 
 	dev = skb->dev;
 
-	if (ipv6_get_lladdr(dev, &saddr_buf)) {
+	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
 		ND_PRINTK2(KERN_WARNING
 			   "ICMPv6 Redirect: no link-local address on %s\n",
 			   dev->name);

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-25 19:45               ` Neil Horman
@ 2007-01-25 20:18                 ` Vlad Yasevich
  2007-01-25 21:26                   ` Neil Horman
  2007-01-26  0:13                 ` YOSHIFUJI Hideaki / 吉藤英明
  1 sibling, 1 reply; 71+ messages in thread
From: Vlad Yasevich @ 2007-01-25 20:18 UTC (permalink / raw)
  To: Neil Horman
  Cc: Sridhar Samudrala,
	YOSHIFUJI Hideaki / 吉藤英明,
	davem, kuznet, pekkas, jmorris, kaber, netdev

Hi Neil

> @@ -1027,15 +1029,17 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
>  				}
>  			}
>  
> -			/* Rule 3: Avoid deprecated address */
> +			/* Rule 3: Avoid deprecated and optimistic address */
>  			if (hiscore.rule < 3) {
>  				if (ipv6_saddr_preferred(hiscore.addr_type) ||
> -				    !(ifa_result->flags & IFA_F_DEPRECATED))
> +				    ((!(ifa_result->flags & IFA_F_DEPRECATED)) && 
> +				    (!(ifa_result->flags & IFA_F_OPTIMISTIC))))

One style comment.  Looks like some extra parenthesis that I don't thing are needed.
I think you can say

+				    (!(ifa_result->flags & IFA_F_DEPRECATED)) && 
+				     !(ifa_result->flags & IFA_F_OPTIMISTIC))))


>  					hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
>  				hiscore.rule++;
>  			}
>  			if (ipv6_saddr_preferred(score.addr_type) ||
> -			    !(ifa->flags & IFA_F_DEPRECATED)) {
> +			    ((!(ifa->flags & IFA_F_DEPRECATED)) &&
> +			    (!(ifa_result->flags & IFA_F_OPTIMISTIC)))) {

same here.

>  				score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
>  				if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
>  					score.rule = 3;

-vlad


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-25 20:18                 ` Vlad Yasevich
@ 2007-01-25 21:26                   ` Neil Horman
  2007-01-25 22:13                     ` Vlad Yasevich
  2007-01-25 22:34                     ` Vlad Yasevich
  0 siblings, 2 replies; 71+ messages in thread
From: Neil Horman @ 2007-01-25 21:26 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: Sridhar Samudrala,
	YOSHIFUJI Hideaki / 吉藤英明,
	davem, kuznet, pekkas, jmorris, kaber, netdev

On Thu, Jan 25, 2007 at 03:18:59PM -0500, Vlad Yasevich wrote:
> Hi Neil
> 
> > @@ -1027,15 +1029,17 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
> >  				}
> >  			}
> >  
> > -			/* Rule 3: Avoid deprecated address */
> > +			/* Rule 3: Avoid deprecated and optimistic address */
> >  			if (hiscore.rule < 3) {
> >  				if (ipv6_saddr_preferred(hiscore.addr_type) ||
> > -				    !(ifa_result->flags & IFA_F_DEPRECATED))
> > +				    ((!(ifa_result->flags & IFA_F_DEPRECATED)) && 
> > +				    (!(ifa_result->flags & IFA_F_OPTIMISTIC))))
> 
> One style comment.  Looks like some extra parenthesis that I don't thing are needed.
> I think you can say
> 
> +				    (!(ifa_result->flags & IFA_F_DEPRECATED)) && 
> +				     !(ifa_result->flags & IFA_F_OPTIMISTIC))))
> 
> 
> >  					hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
> >  				hiscore.rule++;
> >  			}
> >  			if (ipv6_saddr_preferred(score.addr_type) ||
> > -			    !(ifa->flags & IFA_F_DEPRECATED)) {
> > +			    ((!(ifa->flags & IFA_F_DEPRECATED)) &&
> > +			    (!(ifa_result->flags & IFA_F_OPTIMISTIC)))) {
> 
> same here.
> 
> >  				score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
> >  				if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
> >  					score.rule = 3;
> 
> -vlad


I prefer to be more explicit in my order of operation, but that does seem more
consistent with the prevaling style.  New patch attached.

Thanks & Regards
Neil

Signed-off-by: Neil Horman <nhorman@tuxdriver.com>


 include/linux/if_addr.h |    1 
 include/linux/ipv6.h    |    2 +
 include/linux/sysctl.h  |    1 
 include/net/addrconf.h  |    4 +-
 net/ipv6/addrconf.c     |   69 ++++++++++++++++++++++++++++++++--------
 net/ipv6/mcast.c        |    4 +-
 net/ipv6/ndisc.c        |   82 +++++++++++++++++++++++++++++++++++-------------
 7 files changed, 125 insertions(+), 38 deletions(-)



diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
index d557e4c..43f3bed 100644
--- a/include/linux/if_addr.h
+++ b/include/linux/if_addr.h
@@ -39,6 +39,7 @@ enum
 #define IFA_F_TEMPORARY		IFA_F_SECONDARY
 
 #define	IFA_F_NODAD		0x02
+#define IFA_F_OPTIMISTIC	0x04
 #define	IFA_F_HOMEADDRESS	0x10
 #define IFA_F_DEPRECATED	0x20
 #define IFA_F_TENTATIVE		0x40
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index f824113..5d37abf 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -177,6 +177,7 @@ struct ipv6_devconf {
 #endif
 #endif
 	__s32		proxy_ndp;
+	__s32		optimistic_dad;
 	void		*sysctl;
 };
 
@@ -205,6 +206,7 @@ enum {
 	DEVCONF_RTR_PROBE_INTERVAL,
 	DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN,
 	DEVCONF_PROXY_NDP,
+	DEVCONF_OPTIMISTIC_DAD,
 	DEVCONF_MAX
 };
 
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 81480e6..972a33a 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -570,6 +570,7 @@ enum {
 	NET_IPV6_RTR_PROBE_INTERVAL=21,
 	NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22,
 	NET_IPV6_PROXY_NDP=23,
+	NET_IPV6_OPTIMISTIC_DAD=24,
 	__NET_IPV6_MAX
 };
 
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 88df8fc..d248a19 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -73,7 +73,9 @@ extern int			ipv6_get_saddr(struct dst_entry *dst,
 extern int			ipv6_dev_get_saddr(struct net_device *dev, 
 					       struct in6_addr *daddr,
 					       struct in6_addr *saddr);
-extern int			ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
+extern int			ipv6_get_lladdr(struct net_device *dev, 
+						struct in6_addr *,
+						unsigned char banned_flags);
 extern int			ipv6_rcv_saddr_equal(const struct sock *sk, 
 						      const struct sock *sk2);
 extern void			addrconf_join_solict(struct net_device *dev,
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 2a7e461..057a260 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -830,7 +830,8 @@ retry:
 	ift = !max_addresses ||
 	      ipv6_count_addresses(idev) < max_addresses ? 
 		ipv6_add_addr(idev, &addr, tmp_plen,
-			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
+			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
+			      IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
 	if (!ift || IS_ERR(ift)) {
 		in6_ifa_put(ifp);
 		in6_dev_put(idev);
@@ -962,13 +963,14 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
 			 * - Tentative Address (RFC2462 section 5.4)
 			 *  - A tentative address is not considered
 			 *    "assigned to an interface" in the traditional
-			 *    sense.
+			 *    sense, unless it is also flagged as optimistic.
 			 * - Candidate Source Address (section 4)
 			 *  - In any case, anycast addresses, multicast
 			 *    addresses, and the unspecified address MUST
 			 *    NOT be included in a candidate set.
 			 */
-			if (ifa->flags & IFA_F_TENTATIVE)
+			if ((ifa->flags & IFA_F_TENTATIVE) &&
+			    (!(ifa->flags & IFA_F_OPTIMISTIC)))
 				continue;
 			if (unlikely(score.addr_type == IPV6_ADDR_ANY ||
 				     score.addr_type & IPV6_ADDR_MULTICAST)) {
@@ -1027,15 +1029,17 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
 				}
 			}
 
-			/* Rule 3: Avoid deprecated address */
+			/* Rule 3: Avoid deprecated and optimistic address */
 			if (hiscore.rule < 3) {
 				if (ipv6_saddr_preferred(hiscore.addr_type) ||
-				    !(ifa_result->flags & IFA_F_DEPRECATED))
+				    (!(ifa_result->flags & IFA_F_DEPRECATED) && 
+				    !(ifa_result->flags & IFA_F_OPTIMISTIC)))
 					hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				hiscore.rule++;
 			}
 			if (ipv6_saddr_preferred(score.addr_type) ||
-			    !(ifa->flags & IFA_F_DEPRECATED)) {
+			    (!(ifa->flags & IFA_F_DEPRECATED) &&
+			    !(ifa_result->flags & IFA_F_OPTIMISTIC))) {
 				score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
 					score.rule = 3;
@@ -1174,7 +1178,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
 }
 
 
-int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
+int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, 
+		    unsigned char banned_flags)
 {
 	struct inet6_dev *idev;
 	int err = -EADDRNOTAVAIL;
@@ -1185,7 +1190,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
 
 		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 && !(ifp->flags & banned_flags)) {
 				ipv6_addr_copy(addr, &ifp->addr);
 				err = 0;
 				break;
@@ -1751,6 +1756,7 @@ ok:
 
 			update_lft = create = 1;
 			ifp->cstamp = jiffies;
+			ifp->flags |= IFA_F_OPTIMISTIC;
 			addrconf_dad_start(ifp, RTF_ADDRCONF|RTF_PREFIX_RT);
 		}
 
@@ -1945,7 +1951,11 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
 		ifp->prefered_lft = prefered_lft;
 		ifp->tstamp = jiffies;
 		spin_unlock_bh(&ifp->lock);
-
+		/*
+		 * Note that section 3.1 of RFC 4429 indicates
+		 * That the Optimistic flag should not be set for
+		 * manually configured addresses
+		 */
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
 		addrconf_verify(0);
@@ -2123,7 +2133,8 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr
 {
 	struct inet6_ifaddr * ifp;
 
-	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
+	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, 
+		            IFA_F_PERMANENT|IFA_F_OPTIMISTIC);
 	if (!IS_ERR(ifp)) {
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
@@ -2190,7 +2201,7 @@ ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
 {
 	struct in6_addr lladdr;
 
-	if (!ipv6_get_lladdr(link_dev, &lladdr)) {
+	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
 		addrconf_add_linklocal(idev, &lladdr);
 		return 0;
 	}
@@ -2537,8 +2548,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	struct inet6_dev *idev = ifp->idev;
 	struct net_device *dev = idev->dev;
 
+	if (!idev->cnf.optimistic_dad)
+		ifp->flags &= ~IFA_F_OPTIMISTIC;
+
 	addrconf_join_solict(dev, &ifp->addr);
 
+	/*
+	 * Optimistic nodes need to joing the anycast address
+	 * right away
+	 */
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addrconf_join_anycast(ifp);
+
 	if (ifp->prefix_len != 128 && (ifp->flags&IFA_F_PERMANENT))
 		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 0,
 					flags);
@@ -2553,7 +2574,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
 	    !(ifp->flags&IFA_F_TENTATIVE) ||
 	    ifp->flags & IFA_F_NODAD) {
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -2573,6 +2594,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 		addrconf_dad_stop(ifp);
 		return;
 	}
+
+	/*
+	 * Forwarding devices (routers) should not use
+	 * optimistic addresses
+	 * Nor should interfaces that don't know the 
+	 * Source address for their default gateway
+	 * RFC 4429 Sec 3.3
+	 */
+	if ((ipv6_devconf.forwarding) ||
+	   (ifp->rt == NULL))
+		ifp->flags &= ~IFA_F_OPTIMISTIC;
+
 	addrconf_dad_kick(ifp);
 	spin_unlock_bh(&ifp->lock);
 out:
@@ -2597,7 +2630,7 @@ static void addrconf_dad_timer(unsigned long data)
 		 * DAD was successful
 		 */
 
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -3398,6 +3431,7 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
 #endif
 #endif
 	array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp;
+	array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad;
 }
 
 static inline size_t inet6_if_nlmsg_size(void)
@@ -3918,6 +3952,15 @@ static struct addrconf_sysctl_table
 			.proc_handler	=	&proc_dointvec,
 		},
 		{
+			.ctl_name	=	NET_IPV6_OPTIMISTIC_DAD,
+			.procname       =       "optimistic_dad",
+			.data           =       &ipv6_devconf.optimistic_dad,
+			.maxlen         =       sizeof(int),
+			.mode           =       0644,
+			.proc_handler   =       &proc_dointvec,
+
+		},
+		{
 			.ctl_name	=	0,	/* sentinel */
 		}
 	},
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 882cde4..9c5273c 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
@@ -1789,7 +1789,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 6a9f616..fcd22e3 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -498,7 +498,21 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
         msg->icmph.icmp6_unused = 0;
         msg->icmph.icmp6_router    = router;
         msg->icmph.icmp6_solicited = solicited;
-        msg->icmph.icmp6_override  = override;
+	if (!ifp || !(ifp->flags & IFA_F_OPTIMISTIC))
+		msg->icmph.icmp6_override  = override;
+	else {
+		/*
+		 * We must clear the override flag on all
+		 * neighbor advertisements from source 
+		 * addresses that are OPTIMISTIC - RFC 4429
+		 * section 2.2
+		 */
+		if (override)
+			printk(KERN_WARNING
+				"Disallowing override flag for OPTIMISTIC addr\n");
+		msg->icmph.icmp6_override = 0;
+	}
+
 
         /* Set the target address. */
 	ipv6_addr_copy(&msg->target, solicited_addr);
@@ -542,7 +556,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
 	int send_llinfo;
 
 	if (saddr == NULL) {
-		if (ipv6_get_lladdr(dev, &addr_buf))
+		if (ipv6_get_lladdr(dev, &addr_buf,
+			            (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
 			return;
 		saddr = &addr_buf;
 	}
@@ -622,9 +637,20 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
         struct sk_buff *skb;
         struct icmp6hdr *hdr;
 	__u8 * opt;
+	struct inet6_ifaddr *ifp;
         int len;
 	int err;
 
+	/*
+	 * Check the source address.  If its OPTIMISTIC
+	 * and addr_len is non-zero (implying the sllao option)
+	 * then don't send the RS (RFC 4429, section 2.2)
+	 */
+	ifp = ipv6_get_ifaddr(saddr, dev, 1);
+
+	if ((!ifp) || ((ifp->flags & IFA_F_OPTIMISTIC) && dev->addr_len))
+		return;
+
 	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
 			dev->ifindex);
 
@@ -746,6 +772,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	int dad = ipv6_addr_any(saddr);
 	int inc;
 	int is_router;
+	int type;
 
 	if (ipv6_addr_is_multicast(&msg->target)) {
 		ND_PRINTK2(KERN_WARNING 
@@ -796,28 +823,39 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	inc = ipv6_addr_is_multicast(daddr);
 
 	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
-		if (ifp->flags & IFA_F_TENTATIVE) {
-			/* Address is tentative. If the source
-			   is unspecified address, it is someone
-			   does DAD, otherwise we ignore solicitations
-			   until DAD timer expires.
-			 */
-			if (!dad)
+
+		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
+			if (dad) {
+				if (dev->type == ARPHRD_IEEE802_TR) {
+					unsigned char *sadr = skb->mac.raw;
+					if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
+					    sadr[9] == dev->dev_addr[1] &&
+					    sadr[10] == dev->dev_addr[2] &&
+					    sadr[11] == dev->dev_addr[3] &&
+					    sadr[12] == dev->dev_addr[4] &&
+					    sadr[13] == dev->dev_addr[5]) {
+						/* looped-back to us */
+						goto out;
+					}
+				}
+
+				/*
+				 * We are colliding with another node
+				 * who is doing DAD
+				 * so fail our DAD process
+				 */
+				addrconf_dad_failure(ifp);
 				goto out;
-			if (dev->type == ARPHRD_IEEE802_TR) {
-				unsigned char *sadr = skb->mac.raw;
-				if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
-				    sadr[9] == dev->dev_addr[1] &&
-				    sadr[10] == dev->dev_addr[2] &&
-				    sadr[11] == dev->dev_addr[3] &&
-				    sadr[12] == dev->dev_addr[4] &&
-				    sadr[13] == dev->dev_addr[5]) {
-					/* looped-back to us */
+			} else {
+				/*
+				 * This is not a dad solicitation.
+				 * If we are an optimistic node, 
+				 * we should respond.  
+				 * Otherwise, we should ignore it. 
+				 */
+				if (!(ifp->flags & IFA_F_OPTIMISTIC))
 					goto out;
-				}
 			}
-			addrconf_dad_failure(ifp); 
-			return;
 		}
 
 		idev = ifp->idev;
@@ -1406,7 +1444,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
 
 	dev = skb->dev;
 
-	if (ipv6_get_lladdr(dev, &saddr_buf)) {
+	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
 		ND_PRINTK2(KERN_WARNING
 			   "ICMPv6 Redirect: no link-local address on %s\n",
 			   dev->name);

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-25 21:26                   ` Neil Horman
@ 2007-01-25 22:13                     ` Vlad Yasevich
  2007-01-26 14:27                       ` Neil Horman
  2007-01-25 22:34                     ` Vlad Yasevich
  1 sibling, 1 reply; 71+ messages in thread
From: Vlad Yasevich @ 2007-01-25 22:13 UTC (permalink / raw)
  To: Neil Horman
  Cc: Sridhar Samudrala,
	YOSHIFUJI Hideaki / 吉藤英明,
	davem, kuznet, pekkas, jmorris, kaber, netdev

Hi Neil

> 
> I prefer to be more explicit in my order of operation, but that does seem more
> consistent with the prevaling style.  New patch attached.
> 

Looks good to me.

One question thought.  What causes the stack to send via Default Router instead
of sending an NS (Section 3.2).  I see there is code to prevent sending NS from
the optimistic address, but I see nothing that would send this packet to a default
router.  This might be buried somewhere in the routing code, but I just can't see it.


Thanks
-vlad

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-25 21:26                   ` Neil Horman
  2007-01-25 22:13                     ` Vlad Yasevich
@ 2007-01-25 22:34                     ` Vlad Yasevich
  1 sibling, 0 replies; 71+ messages in thread
From: Vlad Yasevich @ 2007-01-25 22:34 UTC (permalink / raw)
  To: Neil Horman
  Cc: Sridhar Samudrala,
	YOSHIFUJI Hideaki / 吉藤英明,
	davem, kuznet, pekkas, jmorris, kaber, netdev

Hi Neil

I went through the RFC again it seems like the following is missing:

Section 3.3:

> * (modifies section 5.4.2) The host MUST join the all-nodes multicast
>    address and the solicited-node multicast address of the
>    Tentative address.  The host SHOULD NOT delay before sending
>    Neighbor Solicitation messages.

For this, addrconf_dad_kick() should pass 0 to addrconf_mod_timer
when the address is optimistic.  Otherwise, we'll delay DAD some of
the purpose of optimistic addresses is lost.

-vlad

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-25 19:45               ` Neil Horman
  2007-01-25 20:18                 ` Vlad Yasevich
@ 2007-01-26  0:13                 ` YOSHIFUJI Hideaki / 吉藤英明
  2007-01-26 14:20                   ` Vlad Yasevich
  2007-01-26 19:18                   ` Neil Horman
  1 sibling, 2 replies; 71+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2007-01-26  0:13 UTC (permalink / raw)
  To: nhorman
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber,
	netdev, yoshfuji

In article <20070125194500.GB8891@hmsreliant.homelinux.net> (at Thu, 25 Jan 2007 14:45:00 -0500), Neil Horman <nhorman@tuxdriver.com> says:

> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index 2a7e461..46f91ee 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -830,7 +830,8 @@ retry:
>  	ift = !max_addresses ||
>  	      ipv6_count_addresses(idev) < max_addresses ? 
>  		ipv6_add_addr(idev, &addr, tmp_plen,
> -			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
> +			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
> +			      IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
>  	if (!ift || IS_ERR(ift)) {
>  		in6_ifa_put(ifp);
>  		in6_dev_put(idev);

If optimistic_dad is disabled, flags should be IFA_F_TEMPORARY,
not IFA_F_TEMPORARY|IFA_F_OPTIMISTIC.

Another idea is to use IFA_F_OPTIMISTIC not
IFA_F_OPTIMISTIC|IFA_F_TENTATIVE until the DAD has been finished.

> @@ -1027,15 +1029,17 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
:
> +			/* Rule 3: Avoid deprecated and optimistic address */
>  			if (hiscore.rule < 3) {
>  				if (ipv6_saddr_preferred(hiscore.addr_type) ||
> -				    !(ifa_result->flags & IFA_F_DEPRECATED))
> +				    ((!(ifa_result->flags & IFA_F_DEPRECATED)) && 
> +				    (!(ifa_result->flags & IFA_F_OPTIMISTIC))))
>  					hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
>  				hiscore.rule++;

                                    ((ifa_result->flags & (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0)

>  			}
>  			if (ipv6_saddr_preferred(score.addr_type) ||
> -			    !(ifa->flags & IFA_F_DEPRECATED)) {
> +			    ((!(ifa->flags & IFA_F_DEPRECATED)) &&
> +			    (!(ifa_result->flags & IFA_F_OPTIMISTIC)))) {
>  				score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
>  				if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
>  					score.rule = 3;

ditto.

> @@ -2123,7 +2133,8 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr
>  {
>  	struct inet6_ifaddr * ifp;
>  
> -	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
> +	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, 
> +		            IFA_F_PERMANENT|IFA_F_OPTIMISTIC);
>  	if (!IS_ERR(ifp)) {
>  		addrconf_dad_start(ifp, 0);
>  		in6_ifa_put(ifp);

Please do not always put IFA_F_OPTIMISTIC.

>  
> +	/*
> +	 * Optimistic nodes need to joing the anycast address
> +	 * right away
> +	 */
> +	if (ifp->flags & IFA_F_OPTIMISTIC)
> +		addrconf_join_anycast(ifp);
> +
>  	if (ifp->prefix_len != 128 && (ifp->flags&IFA_F_PERMANENT))
>  		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 0,
>  					flags);

Should we join anycast even if the node is a host (not a router)?!

When you add a call to "addrconf_join_anycast()", 
you must consider when to leave this.


> @@ -2573,6 +2594,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
>  		addrconf_dad_stop(ifp);
>  		return;
>  	}
> +
> +	/*
> +	 * Forwarding devices (routers) should not use
> +	 * optimistic addresses
> +	 * Nor should interfaces that don't know the 
> +	 * Source address for their default gateway
> +	 * RFC 4429 Sec 3.3
> +	 */
> +	if ((ipv6_devconf.forwarding) ||
> +	   (ifp->rt == NULL))
> +		ifp->flags &= ~IFA_F_OPTIMISTIC;
> +
>  	addrconf_dad_kick(ifp);
>  	spin_unlock_bh(&ifp->lock);
>  out:

Please test this condition when you are adding the
address.

BTW, you have not implemented the later condition,
right?   Sefault gatewa is not tested.

> index 6a9f616..fcd22e3 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -498,7 +498,21 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
>          msg->icmph.icmp6_unused = 0;
>          msg->icmph.icmp6_router    = router;
>          msg->icmph.icmp6_solicited = solicited;
> -        msg->icmph.icmp6_override  = override;
> +	if (!ifp || !(ifp->flags & IFA_F_OPTIMISTIC))
> +		msg->icmph.icmp6_override  = override;
> +	else {
> +		/*
> +		 * We must clear the override flag on all
> +		 * neighbor advertisements from source 
> +		 * addresses that are OPTIMISTIC - RFC 4429
> +		 * section 2.2
> +		 */
> +		if (override)
> +			printk(KERN_WARNING
> +				"Disallowing override flag for OPTIMISTIC addr\n");
> +		msg->icmph.icmp6_override = 0;
> +	}
> +

Ifp is already put.  Please clear "override" in the code
where we try getting temporary source address for NS.

> @@ -622,9 +637,20 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
> +	/*
> +	 * Check the source address.  If its OPTIMISTIC
> +	 * and addr_len is non-zero (implying the sllao option)
> +	 * then don't send the RS (RFC 4429, section 2.2)
> +	 */
> +	ifp = ipv6_get_ifaddr(saddr, dev, 1);
> +
> +	if ((!ifp) || ((ifp->flags & IFA_F_OPTIMISTIC) && dev->addr_len))
> +		return;
> +
>  	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
>  			dev->ifindex);
>  

I disagree. Please send RS in other way.
Choose another address, or send it without SLLAO.

--yoshfuji

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-26  0:13                 ` YOSHIFUJI Hideaki / 吉藤英明
@ 2007-01-26 14:20                   ` Vlad Yasevich
  2007-01-26 19:18                   ` Neil Horman
  1 sibling, 0 replies; 71+ messages in thread
From: Vlad Yasevich @ 2007-01-26 14:20 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: nhorman, sri, davem, kuznet, pekkas, jmorris, kaber, netdev

YOSHIFUJI Hideaki / 吉藤英明 wrote:
> In article <20070125194500.GB8891@hmsreliant.homelinux.net> (at Thu, 25 Jan 2007 14:45:00 -0500), Neil Horman <nhorman@tuxdriver.com> says:
> 
>> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
>> index 2a7e461..46f91ee 100644
>> --- a/net/ipv6/addrconf.c
>> +++ b/net/ipv6/addrconf.c
>> @@ -830,7 +830,8 @@ retry:
>>  	ift = !max_addresses ||
>>  	      ipv6_count_addresses(idev) < max_addresses ? 
>>  		ipv6_add_addr(idev, &addr, tmp_plen,
>> -			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
>> +			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
>> +			      IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
>>  	if (!ift || IS_ERR(ift)) {
>>  		in6_ifa_put(ifp);
>>  		in6_dev_put(idev);
> 
> If optimistic_dad is disabled, flags should be IFA_F_TEMPORARY,
> not IFA_F_TEMPORARY|IFA_F_OPTIMISTIC.
> 
> Another idea is to use IFA_F_OPTIMISTIC not
> IFA_F_OPTIMISTIC|IFA_F_TENTATIVE until the DAD has been finished.
> 
>> @@ -1027,15 +1029,17 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
> :
>> +			/* Rule 3: Avoid deprecated and optimistic address */
>>  			if (hiscore.rule < 3) {
>>  				if (ipv6_saddr_preferred(hiscore.addr_type) ||
>> -				    !(ifa_result->flags & IFA_F_DEPRECATED))
>> +				    ((!(ifa_result->flags & IFA_F_DEPRECATED)) && 
>> +				    (!(ifa_result->flags & IFA_F_OPTIMISTIC))))
>>  					hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
>>  				hiscore.rule++;
> 
>                                     ((ifa_result->flags & (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0)
> 
>>  			}
>>  			if (ipv6_saddr_preferred(score.addr_type) ||
>> -			    !(ifa->flags & IFA_F_DEPRECATED)) {
>> +			    ((!(ifa->flags & IFA_F_DEPRECATED)) &&
>> +			    (!(ifa_result->flags & IFA_F_OPTIMISTIC)))) {
>>  				score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
>>  				if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
>>  					score.rule = 3;
> 
> ditto.
> 
>> @@ -2123,7 +2133,8 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr
>>  {
>>  	struct inet6_ifaddr * ifp;
>>  
>> -	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
>> +	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, 
>> +		            IFA_F_PERMANENT|IFA_F_OPTIMISTIC);
>>  	if (!IS_ERR(ifp)) {
>>  		addrconf_dad_start(ifp, 0);
>>  		in6_ifa_put(ifp);
> 
> Please do not always put IFA_F_OPTIMISTIC.
> 
>>  
>> +	/*
>> +	 * Optimistic nodes need to joing the anycast address
>> +	 * right away
>> +	 */
>> +	if (ifp->flags & IFA_F_OPTIMISTIC)
>> +		addrconf_join_anycast(ifp);
>> +
>>  	if (ifp->prefix_len != 128 && (ifp->flags&IFA_F_PERMANENT))
>>  		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 0,
>>  					flags);
> 
> Should we join anycast even if the node is a host (not a router)?!

"A router SHOULD NOT configure an Optimistic Address. "
Section 3.3

I think this check is missing from the implementation.


>> @@ -622,9 +637,20 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
>> +	/*
>> +	 * Check the source address.  If its OPTIMISTIC
>> +	 * and addr_len is non-zero (implying the sllao option)
>> +	 * then don't send the RS (RFC 4429, section 2.2)
>> +	 */
>> +	ifp = ipv6_get_ifaddr(saddr, dev, 1);
>> +
>> +	if ((!ifp) || ((ifp->flags & IFA_F_OPTIMISTIC) && dev->addr_len))
>> +		return;
>> +
>>  	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
>>  			dev->ifindex);
>>  
> 
> I disagree. Please send RS in other way.
> Choose another address, or send it without SLLAO.
> 

Agree.  Sending without SLLAO is a "MAY" option in the draft and would
yield better operation.

-vlad

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-25 22:13                     ` Vlad Yasevich
@ 2007-01-26 14:27                       ` Neil Horman
  2007-01-26 15:44                         ` YOSHIFUJI Hideaki / 吉藤英明
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-01-26 14:27 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: Sridhar Samudrala,
	YOSHIFUJI Hideaki / 吉藤英明,
	davem, kuznet, pekkas, jmorris, kaber, netdev

On Thu, Jan 25, 2007 at 05:13:57PM -0500, Vlad Yasevich wrote:
> Hi Neil
> 
> > 
> > I prefer to be more explicit in my order of operation, but that does seem more
> > consistent with the prevaling style.  New patch attached.
> > 
> 
> Looks good to me.
> 
> One question thought.  What causes the stack to send via Default Router instead
> of sending an NS (Section 3.2).  I see there is code to prevent sending NS from
> the optimistic address, but I see nothing that would send this packet to a default
> router.  This might be buried somewhere in the routing code, but I just can't see it.
> 
> 
I'm looking for it at the moment, but I too had assumed that redirecting the
outgoing packet to the default router would happen automatically within the
routing code as a result of not having a completed neighbor entry available.
Since I've modified ndisc_send_ns such that we will never send Neighbor
solicitations from an optimistic address, as per section 3.2, we'll never get a
completed neightbor entry while the address is optimistic.  If thats not the
case, I'd welcome some suggestions on how to implement this (given that I'm not
overly familiar with the code right now).  From what I see, I think the routing
code will select the default route when rti6_nexthop is null during route
selection, which it will be if the neighbor entry doesn't get resolved.  Not
100% sure though.  Let me know what you think.

Regards
Neil

> Thanks
> -vlad

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-26 14:27                       ` Neil Horman
@ 2007-01-26 15:44                         ` YOSHIFUJI Hideaki / 吉藤英明
  2007-01-26 19:03                           ` Neil Horman
  0 siblings, 1 reply; 71+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2007-01-26 15:44 UTC (permalink / raw)
  To: nhorman
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber,
	netdev, yoshfuji

In article <20070126142730.GA15916@hmsreliant.homelinux.net> (at Fri, 26 Jan 2007 09:27:30 -0500), Neil Horman <nhorman@tuxdriver.com> says:

> I'm looking for it at the moment, but I too had assumed that redirecting the
> outgoing packet to the default router would happen automatically within the
> routing code as a result of not having a completed neighbor entry available.
> Since I've modified ndisc_send_ns such that we will never send Neighbor
> solicitations from an optimistic address, as per section 3.2, we'll never get a
> completed neightbor entry while the address is optimistic.  If thats not the
> case, I'd welcome some suggestions on how to implement this (given that I'm not
> overly familiar with the code right now).  From what I see, I think the routing
> code will select the default route when rti6_nexthop is null during route
> selection, which it will be if the neighbor entry doesn't get resolved.  Not
> 100% sure though.  Let me know what you think.

(Now I remember that I hit similar issue before when I once
tried to implement this....)

Well... no, you cannot assume that routing code solves this issue.
This is not so trivial, and I am not aware clean solution yet...

On reason of this issue is because the source address may not be
probided by user, and the source address selection will be done
after looking up routing table.

One possibiliy is to solve by rt->rt6i_nexthop->output(),
but I guess we will hit some race condition.

Hmm...

--yoshfuji

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-26 15:44                         ` YOSHIFUJI Hideaki / 吉藤英明
@ 2007-01-26 19:03                           ` Neil Horman
  0 siblings, 0 replies; 71+ messages in thread
From: Neil Horman @ 2007-01-26 19:03 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber, netdev

On Sat, Jan 27, 2007 at 12:44:29AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
> In article <20070126142730.GA15916@hmsreliant.homelinux.net> (at Fri, 26 Jan 2007 09:27:30 -0500), Neil Horman <nhorman@tuxdriver.com> says:
> 
> > I'm looking for it at the moment, but I too had assumed that redirecting the
> > outgoing packet to the default router would happen automatically within the
> > routing code as a result of not having a completed neighbor entry available.
> > Since I've modified ndisc_send_ns such that we will never send Neighbor
> > solicitations from an optimistic address, as per section 3.2, we'll never get a
> > completed neightbor entry while the address is optimistic.  If thats not the
> > case, I'd welcome some suggestions on how to implement this (given that I'm not
> > overly familiar with the code right now).  From what I see, I think the routing
> > code will select the default route when rti6_nexthop is null during route
> > selection, which it will be if the neighbor entry doesn't get resolved.  Not
> > 100% sure though.  Let me know what you think.
> 
> (Now I remember that I hit similar issue before when I once
> tried to implement this....)
> 
> Well... no, you cannot assume that routing code solves this issue.
> This is not so trivial, and I am not aware clean solution yet...
> 
> On reason of this issue is because the source address may not be
> probided by user, and the source address selection will be done
> after looking up routing table.
> 
> One possibiliy is to solve by rt->rt6i_nexthop->output(),
> but I guess we will hit some race condition.
> 
> Hmm...
> 
Is this perhaps something we can do in ip6_output2?  We can test the interface
flags there and if the interface is optimistic, we can lookup the default route
(perhaps via rt6_lookup, using the unspecified address for the daddr), and then
replace skb->dst with the dst_entry retrieved from the lookup, which should give
us the proper mac header construction starting in ip6_output_finish.

What do you think?
Neil


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-26  0:13                 ` YOSHIFUJI Hideaki / 吉藤英明
  2007-01-26 14:20                   ` Vlad Yasevich
@ 2007-01-26 19:18                   ` Neil Horman
  2007-01-26 20:28                     ` Vlad Yasevich
  1 sibling, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-01-26 19:18 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber, netdev

On Fri, Jan 26, 2007 at 09:13:31AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
> In article <20070125194500.GB8891@hmsreliant.homelinux.net> (at Thu, 25 Jan 2007 14:45:00 -0500), Neil Horman <nhorman@tuxdriver.com> says:


New patch attached with most of your suggestions incorporated.  I've a few
comments mixed in for some of the suggestions that I think need further
discussion

> If optimistic_dad is disabled, flags should be IFA_F_TEMPORARY,
> not IFA_F_TEMPORARY|IFA_F_OPTIMISTIC.
> 
> Another idea is to use IFA_F_OPTIMISTIC not
> IFA_F_OPTIMISTIC|IFA_F_TENTATIVE until the DAD has been finished.
> 

I'm currently setting the OPTIMISTIC flag in every location that its possibly
needed, and then clearing it in addrconf_dad_start if that interface is not
participating in optimistic dad.  I do this because the RFC in section 3.1
indicates that manually configured addresses should not set the optimistic flag.
If I removed the OPTIMISTIC flag from the locations it gets set in the patch and
then only set it for participating interfaces in addrconf_dad_start, I would
need to have some way to tell if the address in question was manually configured
(to avoid setting it in that case).  At present I see no clear way to do that,
but if you have a suggestion, I'll happily change this around.

<snip>
> 
>                                     ((ifa_result->flags & (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0)
<snip>
> ditto.
> 
Done

> > @@ -2123,7 +2133,8 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr
> >  {
> >  	struct inet6_ifaddr * ifp;
> >  
> > -	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
> > +	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, 
> > +		            IFA_F_PERMANENT|IFA_F_OPTIMISTIC);
> >  	if (!IS_ERR(ifp)) {
> >  		addrconf_dad_start(ifp, 0);
> >  		in6_ifa_put(ifp);
> 
> Please do not always put IFA_F_OPTIMISTIC.
> 
Again, same reasoning as above for why I set optimistic in this way.

> >  
> > +	/*
> > +	 * Optimistic nodes need to joing the anycast address
> > +	 * right away
> > +	 */
> > +	if (ifp->flags & IFA_F_OPTIMISTIC)
> > +		addrconf_join_anycast(ifp);
> > +
> >  	if (ifp->prefix_len != 128 && (ifp->flags&IFA_F_PERMANENT))
> >  		addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 0,
> >  					flags);
> 
> Should we join anycast even if the node is a host (not a router)?!
> 
I've moved this to the spot below where we check if we are forwarding, so the
new patch attached does not join anycast if we are a router.

> When you add a call to "addrconf_join_anycast()", 
> you must consider when to leave this.
> 
I think addrconf_dad_completed is appropriate for this.  New patch leaves
anycast group when we are no longer optimistic

> 
> > @@ -2573,6 +2594,18 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
> >  		addrconf_dad_stop(ifp);
> >  		return;
> >  	}
> > +
> > +	/*
> > +	 * Forwarding devices (routers) should not use
> > +	 * optimistic addresses
> > +	 * Nor should interfaces that don't know the 
> > +	 * Source address for their default gateway
> > +	 * RFC 4429 Sec 3.3
> > +	 */
> > +	if ((ipv6_devconf.forwarding) ||
> > +	   (ifp->rt == NULL))
> > +		ifp->flags &= ~IFA_F_OPTIMISTIC;
> > +
> >  	addrconf_dad_kick(ifp);
> >  	spin_unlock_bh(&ifp->lock);
> >  out:
> 
> Please test this condition when you are adding the
> address.
> 
Are you sure?  There are several locations where we add an address that we start
dad on.  Shall I test this in all of those places?  It would seem more
appropriate to do it in addrconf_dad_start, as I do.  Or am I missing something?


> BTW, you have not implemented the later condition,
> right?   Sefault gatewa is not tested.
> 
I thought I was testing it.  I was under the impression from my reading of the
code the ifp->rt held the rt6_info pointer for the default gateway, so if its
not set, we don't know the default gateway.  I may well be missing something
here though.  Please let me know.

> > index 6a9f616..fcd22e3 100644
> > --- a/net/ipv6/ndisc.c
> > +++ b/net/ipv6/ndisc.c
> > @@ -498,7 +498,21 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
> >          msg->icmph.icmp6_unused = 0;
> >          msg->icmph.icmp6_router    = router;
> >          msg->icmph.icmp6_solicited = solicited;
> > -        msg->icmph.icmp6_override  = override;
> > +	if (!ifp || !(ifp->flags & IFA_F_OPTIMISTIC))
> > +		msg->icmph.icmp6_override  = override;
> > +	else {
> > +		/*
> > +		 * We must clear the override flag on all
> > +		 * neighbor advertisements from source 
> > +		 * addresses that are OPTIMISTIC - RFC 4429
> > +		 * section 2.2
> > +		 */
> > +		if (override)
> > +			printk(KERN_WARNING
> > +				"Disallowing override flag for OPTIMISTIC addr\n");
> > +		msg->icmph.icmp6_override = 0;
> > +	}
> > +
> 
> Ifp is already put.  Please clear "override" in the code
> where we try getting temporary source address for NS.
> 
Done.  I've moved the check to before the in6_ifa_put, and added a flag to skip
adding the sllao option if we find we are optimistic.

> > @@ -622,9 +637,20 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
> > +	/*
> > +	 * Check the source address.  If its OPTIMISTIC
> > +	 * and addr_len is non-zero (implying the sllao option)
> > +	 * then don't send the RS (RFC 4429, section 2.2)
> > +	 */
> > +	ifp = ipv6_get_ifaddr(saddr, dev, 1);
> > +
> > +	if ((!ifp) || ((ifp->flags & IFA_F_OPTIMISTIC) && dev->addr_len))
> > +		return;
> > +
> >  	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
> >  			dev->ifindex);
> >  
> 
> I disagree. Please send RS in other way.
> Choose another address, or send it without SLLAO.
> 
Done



More thoughts/comments appreciated.

Thanks & Regards
Neil


Signed-off-by: Neil Horman <nhorman@tuxdriver.com>


 include/linux/if_addr.h |    1 
 include/linux/ipv6.h    |    2 +
 include/linux/sysctl.h  |    1 
 include/net/addrconf.h  |    4 +-
 net/ipv6/addrconf.c     |   78 +++++++++++++++++++++++++++++++++++++++---------
 net/ipv6/mcast.c        |    4 +-
 net/ipv6/ndisc.c        |   74 +++++++++++++++++++++++++++++++--------------
 7 files changed, 124 insertions(+), 40 deletions(-)



diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
index d557e4c..43f3bed 100644
--- a/include/linux/if_addr.h
+++ b/include/linux/if_addr.h
@@ -39,6 +39,7 @@ enum
 #define IFA_F_TEMPORARY		IFA_F_SECONDARY
 
 #define	IFA_F_NODAD		0x02
+#define IFA_F_OPTIMISTIC	0x04
 #define	IFA_F_HOMEADDRESS	0x10
 #define IFA_F_DEPRECATED	0x20
 #define IFA_F_TENTATIVE		0x40
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index f824113..5d37abf 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -177,6 +177,7 @@ struct ipv6_devconf {
 #endif
 #endif
 	__s32		proxy_ndp;
+	__s32		optimistic_dad;
 	void		*sysctl;
 };
 
@@ -205,6 +206,7 @@ enum {
 	DEVCONF_RTR_PROBE_INTERVAL,
 	DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN,
 	DEVCONF_PROXY_NDP,
+	DEVCONF_OPTIMISTIC_DAD,
 	DEVCONF_MAX
 };
 
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 81480e6..972a33a 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -570,6 +570,7 @@ enum {
 	NET_IPV6_RTR_PROBE_INTERVAL=21,
 	NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22,
 	NET_IPV6_PROXY_NDP=23,
+	NET_IPV6_OPTIMISTIC_DAD=24,
 	__NET_IPV6_MAX
 };
 
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 88df8fc..d248a19 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -73,7 +73,9 @@ extern int			ipv6_get_saddr(struct dst_entry *dst,
 extern int			ipv6_dev_get_saddr(struct net_device *dev, 
 					       struct in6_addr *daddr,
 					       struct in6_addr *saddr);
-extern int			ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
+extern int			ipv6_get_lladdr(struct net_device *dev, 
+						struct in6_addr *,
+						unsigned char banned_flags);
 extern int			ipv6_rcv_saddr_equal(const struct sock *sk, 
 						      const struct sock *sk2);
 extern void			addrconf_join_solict(struct net_device *dev,
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 2a7e461..fc29815 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -830,7 +830,8 @@ retry:
 	ift = !max_addresses ||
 	      ipv6_count_addresses(idev) < max_addresses ? 
 		ipv6_add_addr(idev, &addr, tmp_plen,
-			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
+			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
+			      IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
 	if (!ift || IS_ERR(ift)) {
 		in6_ifa_put(ifp);
 		in6_dev_put(idev);
@@ -962,13 +963,14 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
 			 * - Tentative Address (RFC2462 section 5.4)
 			 *  - A tentative address is not considered
 			 *    "assigned to an interface" in the traditional
-			 *    sense.
+			 *    sense, unless it is also flagged as optimistic.
 			 * - Candidate Source Address (section 4)
 			 *  - In any case, anycast addresses, multicast
 			 *    addresses, and the unspecified address MUST
 			 *    NOT be included in a candidate set.
 			 */
-			if (ifa->flags & IFA_F_TENTATIVE)
+			if ((ifa->flags & IFA_F_TENTATIVE) &&
+			    (!(ifa->flags & IFA_F_OPTIMISTIC)))
 				continue;
 			if (unlikely(score.addr_type == IPV6_ADDR_ANY ||
 				     score.addr_type & IPV6_ADDR_MULTICAST)) {
@@ -1027,15 +1029,17 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
 				}
 			}
 
-			/* Rule 3: Avoid deprecated address */
+			/* Rule 3: Avoid deprecated and optimistic address */
 			if (hiscore.rule < 3) {
 				if (ipv6_saddr_preferred(hiscore.addr_type) ||
-				    !(ifa_result->flags & IFA_F_DEPRECATED))
+				   ((ifa_result->flags &
+				    (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC) == 0)))
 					hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				hiscore.rule++;
 			}
 			if (ipv6_saddr_preferred(score.addr_type) ||
-			    !(ifa->flags & IFA_F_DEPRECATED)) {
+			   ((ifa_result->flags &
+			    (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC) == 0))) {
 				score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
 					score.rule = 3;
@@ -1174,7 +1178,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
 }
 
 
-int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
+int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, 
+		    unsigned char banned_flags)
 {
 	struct inet6_dev *idev;
 	int err = -EADDRNOTAVAIL;
@@ -1185,7 +1190,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
 
 		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 && !(ifp->flags & banned_flags)) {
 				ipv6_addr_copy(addr, &ifp->addr);
 				err = 0;
 				break;
@@ -1751,6 +1756,7 @@ ok:
 
 			update_lft = create = 1;
 			ifp->cstamp = jiffies;
+			ifp->flags |= IFA_F_OPTIMISTIC;
 			addrconf_dad_start(ifp, RTF_ADDRCONF|RTF_PREFIX_RT);
 		}
 
@@ -1945,7 +1951,11 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
 		ifp->prefered_lft = prefered_lft;
 		ifp->tstamp = jiffies;
 		spin_unlock_bh(&ifp->lock);
-
+		/*
+		 * Note that section 3.1 of RFC 4429 indicates
+		 * That the Optimistic flag should not be set for
+		 * manually configured addresses
+		 */
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
 		addrconf_verify(0);
@@ -2123,7 +2133,8 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr
 {
 	struct inet6_ifaddr * ifp;
 
-	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
+	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, 
+		            IFA_F_PERMANENT|IFA_F_OPTIMISTIC);
 	if (!IS_ERR(ifp)) {
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
@@ -2190,7 +2201,7 @@ ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
 {
 	struct in6_addr lladdr;
 
-	if (!ipv6_get_lladdr(link_dev, &lladdr)) {
+	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
 		addrconf_add_linklocal(idev, &lladdr);
 		return 0;
 	}
@@ -2527,7 +2538,11 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp)
 	unsigned long rand_num;
 	struct inet6_dev *idev = ifp->idev;
 
-	rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		rand_num = 0;
+	else
+		rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+
 	ifp->probes = idev->cnf.dad_transmits;
 	addrconf_mod_timer(ifp, AC_DAD, rand_num);
 }
@@ -2537,6 +2552,9 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	struct inet6_dev *idev = ifp->idev;
 	struct net_device *dev = idev->dev;
 
+	if (!idev->cnf.optimistic_dad)
+		ifp->flags &= ~IFA_F_OPTIMISTIC;
+
 	addrconf_join_solict(dev, &ifp->addr);
 
 	if (ifp->prefix_len != 128 && (ifp->flags&IFA_F_PERMANENT))
@@ -2553,7 +2571,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
 	    !(ifp->flags&IFA_F_TENTATIVE) ||
 	    ifp->flags & IFA_F_NODAD) {
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -2573,6 +2591,25 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 		addrconf_dad_stop(ifp);
 		return;
 	}
+
+	/*
+	 * Forwarding devices (routers) should not use
+	 * optimistic addresses
+	 * Nor should interfaces that don't know the 
+	 * Source address for their default gateway
+	 * RFC 4429 Sec 3.3
+	 */
+	if ((ipv6_devconf.forwarding) ||
+	   (ifp->rt == NULL))
+		ifp->flags &= ~IFA_F_OPTIMISTIC;
+
+	/*
+	 * Optimistic nodes need to join the anycast address
+	 * right away
+	 */
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addrconf_join_anycast(ifp);
+
 	addrconf_dad_kick(ifp);
 	spin_unlock_bh(&ifp->lock);
 out:
@@ -2597,7 +2634,7 @@ static void addrconf_dad_timer(unsigned long data)
 		 * DAD was successful
 		 */
 
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -2627,6 +2664,9 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
 	 *	Configure the address for reception. Now it is valid.
 	 */
 
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addrconf_leave_anycast(ifp);
+
 	ipv6_ifa_notify(RTM_NEWADDR, ifp);
 
 	/* If added prefix is link local and forwarding is off,
@@ -3398,6 +3438,7 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
 #endif
 #endif
 	array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp;
+	array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad;
 }
 
 static inline size_t inet6_if_nlmsg_size(void)
@@ -3918,6 +3959,15 @@ static struct addrconf_sysctl_table
 			.proc_handler	=	&proc_dointvec,
 		},
 		{
+			.ctl_name	=	NET_IPV6_OPTIMISTIC_DAD,
+			.procname       =       "optimistic_dad",
+			.data           =       &ipv6_devconf.optimistic_dad,
+			.maxlen         =       sizeof(int),
+			.mode           =       0644,
+			.proc_handler   =       &proc_dointvec,
+
+		},
+		{
 			.ctl_name	=	0,	/* sentinel */
 		}
 	},
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 882cde4..9c5273c 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
@@ -1789,7 +1789,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 6a9f616..6ed0357 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -447,6 +447,8 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
 	ifp = ipv6_get_ifaddr(solicited_addr, dev, 1);
  	if (ifp) {
 		src_addr = solicited_addr;
+		if (ifp->flags & IFA_F_OPTIMISTIC)
+			override = 0;
 		in6_ifa_put(ifp);
 	} else {
 		if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr))
@@ -498,7 +500,8 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
         msg->icmph.icmp6_unused = 0;
         msg->icmph.icmp6_router    = router;
         msg->icmph.icmp6_solicited = solicited;
-        msg->icmph.icmp6_override  = override;
+	msg->icmph.icmp6_override  = override;
+
 
         /* Set the target address. */
 	ipv6_addr_copy(&msg->target, solicited_addr);
@@ -542,7 +545,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
 	int send_llinfo;
 
 	if (saddr == NULL) {
-		if (ipv6_get_lladdr(dev, &addr_buf))
+		if (ipv6_get_lladdr(dev, &addr_buf,
+			            (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
 			return;
 		saddr = &addr_buf;
 	}
@@ -622,9 +626,21 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
         struct sk_buff *skb;
         struct icmp6hdr *hdr;
 	__u8 * opt;
+	struct inet6_ifaddr *ifp;
+	int include_sllao_option = 1;
         int len;
 	int err;
 
+	/*
+	 * Check the source address.  If its OPTIMISTIC
+	 * and addr_len is non-zero (implying the sllao option)
+	 * then don't send the RS (RFC 4429, section 2.2)
+	 */
+	ifp = ipv6_get_ifaddr(saddr, dev, 1);
+
+	if ((!ifp) || ((ifp->flags & IFA_F_OPTIMISTIC) && dev->addr_len))
+		include_sllao_option=0;
+
 	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
 			dev->ifindex);
 
@@ -664,7 +680,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
 
 	opt = (u8*) (hdr + 1);
 
-	if (dev->addr_len)
+	if (dev->addr_len && include_sllao_option)
 		ndisc_fill_addr_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr,
 				       dev->addr_len, dev->type);
 
@@ -746,6 +762,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	int dad = ipv6_addr_any(saddr);
 	int inc;
 	int is_router;
+	int type;
 
 	if (ipv6_addr_is_multicast(&msg->target)) {
 		ND_PRINTK2(KERN_WARNING 
@@ -796,28 +813,39 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	inc = ipv6_addr_is_multicast(daddr);
 
 	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
-		if (ifp->flags & IFA_F_TENTATIVE) {
-			/* Address is tentative. If the source
-			   is unspecified address, it is someone
-			   does DAD, otherwise we ignore solicitations
-			   until DAD timer expires.
-			 */
-			if (!dad)
+
+		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
+			if (dad) {
+				if (dev->type == ARPHRD_IEEE802_TR) {
+					unsigned char *sadr = skb->mac.raw;
+					if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
+					    sadr[9] == dev->dev_addr[1] &&
+					    sadr[10] == dev->dev_addr[2] &&
+					    sadr[11] == dev->dev_addr[3] &&
+					    sadr[12] == dev->dev_addr[4] &&
+					    sadr[13] == dev->dev_addr[5]) {
+						/* looped-back to us */
+						goto out;
+					}
+				}
+
+				/*
+				 * We are colliding with another node
+				 * who is doing DAD
+				 * so fail our DAD process
+				 */
+				addrconf_dad_failure(ifp);
 				goto out;
-			if (dev->type == ARPHRD_IEEE802_TR) {
-				unsigned char *sadr = skb->mac.raw;
-				if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
-				    sadr[9] == dev->dev_addr[1] &&
-				    sadr[10] == dev->dev_addr[2] &&
-				    sadr[11] == dev->dev_addr[3] &&
-				    sadr[12] == dev->dev_addr[4] &&
-				    sadr[13] == dev->dev_addr[5]) {
-					/* looped-back to us */
+			} else {
+				/*
+				 * This is not a dad solicitation.
+				 * If we are an optimistic node, 
+				 * we should respond.  
+				 * Otherwise, we should ignore it. 
+				 */
+				if (!(ifp->flags & IFA_F_OPTIMISTIC))
 					goto out;
-				}
 			}
-			addrconf_dad_failure(ifp); 
-			return;
 		}
 
 		idev = ifp->idev;
@@ -1406,7 +1434,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
 
 	dev = skb->dev;
 
-	if (ipv6_get_lladdr(dev, &saddr_buf)) {
+	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
 		ND_PRINTK2(KERN_WARNING
 			   "ICMPv6 Redirect: no link-local address on %s\n",
 			   dev->name);

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-26 19:18                   ` Neil Horman
@ 2007-01-26 20:28                     ` Vlad Yasevich
  2007-01-26 21:35                       ` Neil Horman
  0 siblings, 1 reply; 71+ messages in thread
From: Vlad Yasevich @ 2007-01-26 20:28 UTC (permalink / raw)
  To: Neil Horman
  Cc: YOSHIFUJI Hideaki / 吉藤英明,
	sri, davem, kuznet, pekkas, jmorris, kaber, netdev

Hi Neil

Neil Horman wrote:
> On Fri, Jan 26, 2007 at 09:13:31AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
>> In article <20070125194500.GB8891@hmsreliant.homelinux.net> (at Thu, 25 Jan 2007 14:45:00 -0500), Neil Horman <nhorman@tuxdriver.com> says:
> 
> 
> New patch attached with most of your suggestions incorporated.  I've a few
> comments mixed in for some of the suggestions that I think need further
> discussion
> 
>> If optimistic_dad is disabled, flags should be IFA_F_TEMPORARY,
>> not IFA_F_TEMPORARY|IFA_F_OPTIMISTIC.
>>
>> Another idea is to use IFA_F_OPTIMISTIC not
>> IFA_F_OPTIMISTIC|IFA_F_TENTATIVE until the DAD has been finished.
>>
> 
> I'm currently setting the OPTIMISTIC flag in every location that its possibly
> needed, and then clearing it in addrconf_dad_start if that interface is not
> participating in optimistic dad.  I do this because the RFC in section 3.1
> indicates that manually configured addresses should not set the optimistic flag.
> If I removed the OPTIMISTIC flag from the locations it gets set in the patch and
> then only set it for participating interfaces in addrconf_dad_start, I would
> need to have some way to tell if the address in question was manually configured
> (to avoid setting it in that case).  At present I see no clear way to do that,
> but if you have a suggestion, I'll happily change this around.

One suggestiong/question:

Instead of clearing the OPTIMISTIC flag in addrconf_dad_start(), wouldn't it be better
to simply not set the flag in ipv6_add_addr()?  Just mask that flag from the 'flags'
argument passed to that function when conditions are right.

like 

if (!idev->cnf.optimistic_dad || ipv6_devconf.forwarding) 
	flags &= ~IFA_F_OPTIMISTIC;

The ifa->rt is also set there, so if the check for that is valid, we can do it there as well.

-vlad

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-26 20:28                     ` Vlad Yasevich
@ 2007-01-26 21:35                       ` Neil Horman
  2007-01-26 21:42                         ` Vlad Yasevich
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-01-26 21:35 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: YOSHIFUJI Hideaki / 吉藤英明,
	sri, davem, kuznet, pekkas, jmorris, kaber, netdev

On Fri, Jan 26, 2007 at 03:28:40PM -0500, Vlad Yasevich wrote:
> Hi Neil
> 
> Neil Horman wrote:
> > On Fri, Jan 26, 2007 at 09:13:31AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
> >> In article <20070125194500.GB8891@hmsreliant.homelinux.net> (at Thu, 25 Jan 2007 14:45:00 -0500), Neil Horman <nhorman@tuxdriver.com> says:
> > 
> > 
> > New patch attached with most of your suggestions incorporated.  I've a few
> > comments mixed in for some of the suggestions that I think need further
> > discussion
> > 
> >> If optimistic_dad is disabled, flags should be IFA_F_TEMPORARY,
> >> not IFA_F_TEMPORARY|IFA_F_OPTIMISTIC.
> >>
> >> Another idea is to use IFA_F_OPTIMISTIC not
> >> IFA_F_OPTIMISTIC|IFA_F_TENTATIVE until the DAD has been finished.
> >>
> > 
> > I'm currently setting the OPTIMISTIC flag in every location that its possibly
> > needed, and then clearing it in addrconf_dad_start if that interface is not
> > participating in optimistic dad.  I do this because the RFC in section 3.1
> > indicates that manually configured addresses should not set the optimistic flag.
> > If I removed the OPTIMISTIC flag from the locations it gets set in the patch and
> > then only set it for participating interfaces in addrconf_dad_start, I would
> > need to have some way to tell if the address in question was manually configured
> > (to avoid setting it in that case).  At present I see no clear way to do that,
> > but if you have a suggestion, I'll happily change this around.
> 
> One suggestiong/question:
> 
> Instead of clearing the OPTIMISTIC flag in addrconf_dad_start(), wouldn't it be better
> to simply not set the flag in ipv6_add_addr()?  Just mask that flag from the 'flags'
> argument passed to that function when conditions are right.
> 
Doh!  Sometimes I don't just think straight.  Yes, as long as ipv6_add_addr is
only for adding static addresses (which it pretty clearly is), that would work
much better.  I'll fix it up and repost on monday.

Don't suppose you have any thoughts on how to solve the "send to default router"
problem, do you?

Regards
Neil


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-26 21:35                       ` Neil Horman
@ 2007-01-26 21:42                         ` Vlad Yasevich
  2007-01-29 16:34                           ` Neil Horman
  0 siblings, 1 reply; 71+ messages in thread
From: Vlad Yasevich @ 2007-01-26 21:42 UTC (permalink / raw)
  To: Neil Horman
  Cc: YOSHIFUJI Hideaki / 吉藤英明,
	sri, davem, kuznet, pekkas, jmorris, kaber, netdev

Neil Horman wrote:
> On Fri, Jan 26, 2007 at 03:28:40PM -0500, Vlad Yasevich wrote:
>> Hi Neil
>>
>> Neil Horman wrote:
>>> On Fri, Jan 26, 2007 at 09:13:31AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
>>>> In article <20070125194500.GB8891@hmsreliant.homelinux.net> (at Thu, 25 Jan 2007 14:45:00 -0500), Neil Horman <nhorman@tuxdriver.com> says:
>>>
>>> New patch attached with most of your suggestions incorporated.  I've a few
>>> comments mixed in for some of the suggestions that I think need further
>>> discussion
>>>
>>>> If optimistic_dad is disabled, flags should be IFA_F_TEMPORARY,
>>>> not IFA_F_TEMPORARY|IFA_F_OPTIMISTIC.
>>>>
>>>> Another idea is to use IFA_F_OPTIMISTIC not
>>>> IFA_F_OPTIMISTIC|IFA_F_TENTATIVE until the DAD has been finished.
>>>>
>>> I'm currently setting the OPTIMISTIC flag in every location that its possibly
>>> needed, and then clearing it in addrconf_dad_start if that interface is not
>>> participating in optimistic dad.  I do this because the RFC in section 3.1
>>> indicates that manually configured addresses should not set the optimistic flag.
>>> If I removed the OPTIMISTIC flag from the locations it gets set in the patch and
>>> then only set it for participating interfaces in addrconf_dad_start, I would
>>> need to have some way to tell if the address in question was manually configured
>>> (to avoid setting it in that case).  At present I see no clear way to do that,
>>> but if you have a suggestion, I'll happily change this around.
>> One suggestiong/question:
>>
>> Instead of clearing the OPTIMISTIC flag in addrconf_dad_start(), wouldn't it be better
>> to simply not set the flag in ipv6_add_addr()?  Just mask that flag from the 'flags'
>> argument passed to that function when conditions are right.
>>
> Doh!  Sometimes I don't just think straight.  Yes, as long as ipv6_add_addr is
> only for adding static addresses (which it pretty clearly is), that would work
> much better.  I'll fix it up and repost on monday.
> 
> Don't suppose you have any thoughts on how to solve the "send to default router"
> problem, do you?
> 

Still trying to figure how the routing side works. sorry....

-vlad

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-26 21:42                         ` Vlad Yasevich
@ 2007-01-29 16:34                           ` Neil Horman
  2007-01-29 21:30                             ` Neil Horman
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-01-29 16:34 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: YOSHIFUJI Hideaki / 吉藤英明,
	sri, davem, kuznet, pekkas, jmorris, kaber, netdev

On Fri, Jan 26, 2007 at 04:42:41PM -0500, Vlad Yasevich wrote:
> Neil Horman wrote:
> > On Fri, Jan 26, 2007 at 03:28:40PM -0500, Vlad Yasevich wrote:
> >> Hi Neil
> >>
> >> Neil Horman wrote:
> >>> On Fri, Jan 26, 2007 at 09:13:31AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
> >>>> In article <20070125194500.GB8891@hmsreliant.homelinux.net> (at Thu, 25 Jan 2007 14:45:00 -0500), Neil Horman <nhorman@tuxdriver.com> says:
> >>>
> >>> New patch attached with most of your suggestions incorporated.  I've a few
> >>> comments mixed in for some of the suggestions that I think need further
> >>> discussion
> >>>
> >>>> If optimistic_dad is disabled, flags should be IFA_F_TEMPORARY,
> >>>> not IFA_F_TEMPORARY|IFA_F_OPTIMISTIC.
> >>>>
> >>>> Another idea is to use IFA_F_OPTIMISTIC not
> >>>> IFA_F_OPTIMISTIC|IFA_F_TENTATIVE until the DAD has been finished.
> >>>>
> >>> I'm currently setting the OPTIMISTIC flag in every location that its possibly
> >>> needed, and then clearing it in addrconf_dad_start if that interface is not
> >>> participating in optimistic dad.  I do this because the RFC in section 3.1
> >>> indicates that manually configured addresses should not set the optimistic flag.
> >>> If I removed the OPTIMISTIC flag from the locations it gets set in the patch and
> >>> then only set it for participating interfaces in addrconf_dad_start, I would
> >>> need to have some way to tell if the address in question was manually configured
> >>> (to avoid setting it in that case).  At present I see no clear way to do that,
> >>> but if you have a suggestion, I'll happily change this around.
> >> One suggestiong/question:
> >>
> >> Instead of clearing the OPTIMISTIC flag in addrconf_dad_start(), wouldn't it be better
> >> to simply not set the flag in ipv6_add_addr()?  Just mask that flag from the 'flags'
> >> argument passed to that function when conditions are right.
> >>
> > Doh!  Sometimes I don't just think straight.  Yes, as long as ipv6_add_addr is
> > only for adding static addresses (which it pretty clearly is), that would work
> > much better.  I'll fix it up and repost on monday.
> > 
> > Don't suppose you have any thoughts on how to solve the "send to default router"
> > problem, do you?
> > 
> 
> Still trying to figure how the routing side works. sorry....
> 
> -vlad
I've got your flag changes made, but I'm holding off on reposting for a bit.
Dave M. just gave me some advice on how best to handle the default router case.
Trying to understand the routing code and integrate that before I repost.

Thanks
Neil


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-29 16:34                           ` Neil Horman
@ 2007-01-29 21:30                             ` Neil Horman
  2007-01-29 22:25                               ` YOSHIFUJI Hideaki / 吉藤英明
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-01-29 21:30 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: YOSHIFUJI Hideaki / 吉藤英明,
	sri, davem, kuznet, pekkas, jmorris, kaber, netdev


Quick reality check here.  In thinking about how best to go about this
redirection of frames to the default router, based on Dave M.s input, I think
that the best solution would be in ndisc_send_ns.  What I was thinking was that
in ndisc_send_ns, we already detect if a source address is optimistic and squash
the transmission of the frame there.  What if in addition to that supression, we
also immediately update the neighbor cache entry for the requested host, to
reflect the mac address of the default gateway.  That way any dst cache lookups
for hosts we don't know the true link layer address for will get redirected to
the default gateway.  Then any ICMP redirect messages from the router will fix
those entires up for us.  Later, when DAD completes, normal neighbour garbage
collection and/or timeouts will sort out the neighbor cache entries for us.  Or
alternatively we could flush the cache entirely, or scan the cache for entires
that match the lladdr of the default gateway and do a selective purge.  I'm
going to start implementing this, but I wanted to post it here to make sure I'm
not missing anything really big on this idea.

Thanks & Regards
Neil


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-29 21:30                             ` Neil Horman
@ 2007-01-29 22:25                               ` YOSHIFUJI Hideaki / 吉藤英明
  2007-01-30 13:02                                 ` Neil Horman
  0 siblings, 1 reply; 71+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2007-01-29 22:25 UTC (permalink / raw)
  To: nhorman
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber,
	netdev, yoshfuji

In article <20070129213013.GA26841@hmsreliant.homelinux.net> (at Mon, 29 Jan 2007 16:30:13 -0500), Neil Horman <nhorman@tuxdriver.com> says:

> Quick reality check here.  In thinking about how best to go about this
> redirection of frames to the default router, based on Dave M.s input, I think
> that the best solution would be in ndisc_send_ns.  What I was thinking was that
> in ndisc_send_ns, we already detect if a source address is optimistic and squash
> the transmission of the frame there.  What if in addition to that supression, we
:

Well...I think it is okay if sending NS is deferred (or omit) in
ndisc_send_ns() (or in ndisc_solicit(), probably) if the source is
optimistic address, but... I'm not sure so far if it is appropriate
from design POV. The ndisc_send_ns() nor ndisc_solicit() is not about
our current neigh state machine, at all.

I do not think we should copy neighbor information from (one of)
default routers, but use temporary neigh entry (or neigh in new state)
for such datagrams in stead.  We should aware that:

 1) default router's link-layer address may change.
 2) we may have more than one default routers.
 3) the default router's link-layer may be invalidated.

Anyway, I'm start thinking about CONFIG_IPV6_OPTIMISTIC_DAD to 
make sure the new code path will not break anything else...

--yoshfuji



^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-29 22:25                               ` YOSHIFUJI Hideaki / 吉藤英明
@ 2007-01-30 13:02                                 ` Neil Horman
  2007-01-30 16:16                                   ` YOSHIFUJI Hideaki / 吉藤英明
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-01-30 13:02 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber, netdev

On Tue, Jan 30, 2007 at 07:25:36AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
> In article <20070129213013.GA26841@hmsreliant.homelinux.net> (at Mon, 29 Jan 2007 16:30:13 -0500), Neil Horman <nhorman@tuxdriver.com> says:
> 
> > Quick reality check here.  In thinking about how best to go about this
> > redirection of frames to the default router, based on Dave M.s input, I think
> > that the best solution would be in ndisc_send_ns.  What I was thinking was that
> > in ndisc_send_ns, we already detect if a source address is optimistic and squash
> > the transmission of the frame there.  What if in addition to that supression, we
> :
> 
> Well...I think it is okay if sending NS is deferred (or omit) in
> ndisc_send_ns() (or in ndisc_solicit(), probably) if the source is
Agreed, the code already does this, per the RFC.

> optimistic address, but... I'm not sure so far if it is appropriate
> from design POV. The ndisc_send_ns() nor ndisc_solicit() is not about
> our current neigh state machine, at all.
> 
Thats true, but I'm not sure how we can avoid that.

> I do not think we should copy neighbor information from (one of)
> default routers, but use temporary neigh entry (or neigh in new state)
> for such datagrams in stead.  We should aware that:
> 
Not sure how that is different from what I'm proposing.  a neighbor entry that
maps a given host on the current subnet to the MAC of the default router, that
then gets flushed when DAD completes is temporary, as far as I can see.

>  1) default router's link-layer address may change.
True, but if this changes, all our network connectivity is lost, until the
normal neighbor solicitation process completes anyway.

>  2) we may have more than one default routers.
True, but I would think we could select any of them and this would work.
Granted, we wouldn't use all the default routers in the table as we would with
routed frames, but I'm not sure how we avoid that.

>  3) the default router's link-layer may be invalidated.
> 
yes, but this would be bad for the same reason as (1)

> Anyway, I'm start thinking about CONFIG_IPV6_OPTIMISTIC_DAD to 
> make sure the new code path will not break anything else...
> 
Agreed, I'll add in the config option with my next patch.

> --yoshfuji
> 

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-30 13:02                                 ` Neil Horman
@ 2007-01-30 16:16                                   ` YOSHIFUJI Hideaki / 吉藤英明
  2007-01-31 20:54                                     ` Neil Horman
  0 siblings, 1 reply; 71+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2007-01-30 16:16 UTC (permalink / raw)
  To: nhorman
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber,
	netdev, yoshfuji

In article <20070130130208.GA3723@hmsreliant.homelinux.net> (at Tue, 30 Jan 2007 08:02:08 -0500), Neil Horman <nhorman@tuxdriver.com> says:

> > I do not think we should copy neighbor information from (one of)
> > default routers, but use temporary neigh entry (or neigh in new state)
> > for such datagrams in stead.  We should aware that:
> > 
> Not sure how that is different from what I'm proposing.  a neighbor entry that
> maps a given host on the current subnet to the MAC of the default router, that
> then gets flushed when DAD completes is temporary, as far as I can see.
> 
> >  1) default router's link-layer address may change.
> True, but if this changes, all our network connectivity is lost, until the
> normal neighbor solicitation process completes anyway.

No, router may update its link-layer address by NA with Override flag set.
In that case, we must use new link-layer for subsequent packets from
our opportunistic address duing DAD.

> >  2) we may have more than one default routers.
> True, but I would think we could select any of them and this would work.
> Granted, we wouldn't use all the default routers in the table as we would with
> routed frames, but I'm not sure how we avoid that.

I mean, if the status of the selected default router has changed or 
has been deleted, we should try other router, at least.

> >  3) the default router's link-layer may be invalidated.
> > 
> yes, but this would be bad for the same reason as (1)

We MUST take this into account.

--yoshfuji

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-30 16:16                                   ` YOSHIFUJI Hideaki / 吉藤英明
@ 2007-01-31 20:54                                     ` Neil Horman
  2007-02-02 19:06                                       ` Neil Horman
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-01-31 20:54 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber, netdev

On Wed, Jan 31, 2007 at 01:16:29AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
> In article <20070130130208.GA3723@hmsreliant.homelinux.net> (at Tue, 30 Jan 2007 08:02:08 -0500), Neil Horman <nhorman@tuxdriver.com> says:
> 
> > > I do not think we should copy neighbor information from (one of)
> > > default routers, but use temporary neigh entry (or neigh in new state)
> > > for such datagrams in stead.  We should aware that:
> > > 
> > Not sure how that is different from what I'm proposing.  a neighbor entry that
> > maps a given host on the current subnet to the MAC of the default router, that
> > then gets flushed when DAD completes is temporary, as far as I can see.
> > 
> > >  1) default router's link-layer address may change.
> > True, but if this changes, all our network connectivity is lost, until the
> > normal neighbor solicitation process completes anyway.
> 
> No, router may update its link-layer address by NA with Override flag set.
> In that case, we must use new link-layer for subsequent packets from
> our opportunistic address duing DAD.
> 
> > >  2) we may have more than one default routers.
> > True, but I would think we could select any of them and this would work.
> > Granted, we wouldn't use all the default routers in the table as we would with
> > routed frames, but I'm not sure how we avoid that.
> 
> I mean, if the status of the selected default router has changed or 
> has been deleted, we should try other router, at least.
> 
> > >  3) the default router's link-layer may be invalidated.
> > > 
> > yes, but this would be bad for the same reason as (1)
> 
> We MUST take this into account.
> 

Ok, understood.  In summary, we can't simply fill out the neighbor entry for a
given host with the address of a default router because

1) Any changes to the router link layer address wont be reflected in that when
it (the change) happens, redering the local address unable to communicate until
the neighbour entries age out.

2) There may be more than one default router as selected by the routing policy,
and we should be able to try those routes if the selected router becomes
unavailable


It sounds like what we need to do is shim into the route lookup code, and detect
there if we need to redirect every packet as they are sent.  I think I may have
an idea for this.  What if we implement the lookup in ip6_dst_lookup_tail.  In
there we can check if the associated neigh entry for the resolved dst_entry is
in a INCOMPLETE or FAILED state, and if the ifaddr associated withe the source
address passed in via the flowi struct is in an OPTIMISTIC state.  If both
conditions are true, we release the dst_entry that we origionally looked up, and
instead conduct a second lookup with a newly constructed flowi struct with the
dst addr replaced with an all zeros address (which I think should return a
gateway, selected as per routing policy).  Then we can return that dst entry
instead.  Since ip6_push_pending frames seems to bulid the ipv6 header using the
addresses in the flowi struct passed into ip6_*_dst_lookup from *_sendmsg
routines, and the link layer header is built based on the info gathered from the
resolved dst_entry (If I'm reading the code right), I think this give us what we
want, in that we redirect to a gatweway when we send from an optimistic address,
and we select the gateway based on the logic in the route lookup code
(ip6_route_output() specifically).  I'm trying to implement this right now, and
will post a new patch as soon as I have it done.  In the meantime, any thoughts
would be most appreciated.

Regards
Neil

> --yoshfuji
> -
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-01-31 20:54                                     ` Neil Horman
@ 2007-02-02 19:06                                       ` Neil Horman
  2007-02-02 19:46                                         ` David Miller
                                                           ` (2 more replies)
  0 siblings, 3 replies; 71+ messages in thread
From: Neil Horman @ 2007-02-02 19:06 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber, netdev


Ok, I'm still testing it, but heres a new patch for review.  Significant changes
include the addition of a CONFIG_IPV6_OPTIMISTIC_DAD option that is dependent on
the inclusion of both IPPV6 and EXPERIMENTAL options, as well as a new method
for redirecting packets from optimistic sources to incomplete neighbors by
instead looking up a default router in ip6_dst_lookup_tail, as I described in my
previous note.

Thoughts and comments appreciated
Thanks & Regards
Neil


Signed-off-by: Neil Horman <nhorman@tuxdriver.com>


 include/linux/if_addr.h |    1 
 include/linux/ipv6.h    |    4 ++
 include/linux/sysctl.h  |    1 
 include/net/addrconf.h  |    4 +-
 net/ipv6/Kconfig        |   10 ++++++
 net/ipv6/addrconf.c     |   79 ++++++++++++++++++++++++++++++++++++++----------
 net/ipv6/ip6_output.c   |   32 ++++++++++++++++++-
 net/ipv6/mcast.c        |    4 +-
 net/ipv6/ndisc.c        |   79 +++++++++++++++++++++++++++++++++---------------
 9 files changed, 171 insertions(+), 43 deletions(-)



diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
index d557e4c..43f3bed 100644
--- a/include/linux/if_addr.h
+++ b/include/linux/if_addr.h
@@ -39,6 +39,7 @@ enum
 #define IFA_F_TEMPORARY		IFA_F_SECONDARY
 
 #define	IFA_F_NODAD		0x02
+#define IFA_F_OPTIMISTIC	0x04
 #define	IFA_F_HOMEADDRESS	0x10
 #define IFA_F_DEPRECATED	0x20
 #define IFA_F_TENTATIVE		0x40
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index f824113..bf93c1b 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -177,6 +177,9 @@ struct ipv6_devconf {
 #endif
 #endif
 	__s32		proxy_ndp;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	__s32		optimistic_dad;
+#endif
 	void		*sysctl;
 };
 
@@ -205,6 +208,7 @@ enum {
 	DEVCONF_RTR_PROBE_INTERVAL,
 	DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN,
 	DEVCONF_PROXY_NDP,
+	DEVCONF_OPTIMISTIC_DAD,
 	DEVCONF_MAX
 };
 
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 81480e6..972a33a 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -570,6 +570,7 @@ enum {
 	NET_IPV6_RTR_PROBE_INTERVAL=21,
 	NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22,
 	NET_IPV6_PROXY_NDP=23,
+	NET_IPV6_OPTIMISTIC_DAD=24,
 	__NET_IPV6_MAX
 };
 
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 88df8fc..d248a19 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -73,7 +73,9 @@ extern int			ipv6_get_saddr(struct dst_entry *dst,
 extern int			ipv6_dev_get_saddr(struct net_device *dev, 
 					       struct in6_addr *daddr,
 					       struct in6_addr *saddr);
-extern int			ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
+extern int			ipv6_get_lladdr(struct net_device *dev, 
+						struct in6_addr *,
+						unsigned char banned_flags);
 extern int			ipv6_rcv_saddr_equal(const struct sock *sk, 
 						      const struct sock *sk2);
 extern void			addrconf_join_solict(struct net_device *dev,
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index deb4101..822d3eb 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -57,6 +57,16 @@ config IPV6_ROUTE_INFO
 
 	  If unsure, say N.
 
+config IPV6_OPTIMISTIC_DAD
+	bool "IPv6: Enable RFC 4429 Optimistic DAD (EXPERIMENTAL)"
+	depends on IPV6 && EXPERIMENTAL
+	---help---
+	  This is experimental support for optimistic Duplicate
+	  Address Detection.  It allows for autoconfigured addresses
+	  to be used more quickly.
+
+	  If unsure, say N.
+
 config INET6_AH
 	tristate "IPv6: AH transformation"
 	depends on IPV6
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 2a7e461..d00e3f6 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -593,7 +593,13 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
 	ifa->cstamp = ifa->tstamp = jiffies;
 
 	ifa->rt = rt;
-
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	if (!idev->cnf.optimistic_dad || ipv6_devconf.forwarding ||
+	   (ifa->rt->rt6i_nexthop == NULL))
+		ifa->flags &= ~IFA_F_OPTIMISTIC;
+#else
+	ifa->flags &= ~IFA_F_OPTIMISTIC;
+#endif
 	ifa->idev = idev;
 	in6_dev_hold(idev);
 	/* For caller */
@@ -830,7 +836,8 @@ retry:
 	ift = !max_addresses ||
 	      ipv6_count_addresses(idev) < max_addresses ? 
 		ipv6_add_addr(idev, &addr, tmp_plen,
-			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
+			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
+			      IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
 	if (!ift || IS_ERR(ift)) {
 		in6_ifa_put(ifp);
 		in6_dev_put(idev);
@@ -962,13 +969,14 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
 			 * - Tentative Address (RFC2462 section 5.4)
 			 *  - A tentative address is not considered
 			 *    "assigned to an interface" in the traditional
-			 *    sense.
+			 *    sense, unless it is also flagged as optimistic.
 			 * - Candidate Source Address (section 4)
 			 *  - In any case, anycast addresses, multicast
 			 *    addresses, and the unspecified address MUST
 			 *    NOT be included in a candidate set.
 			 */
-			if (ifa->flags & IFA_F_TENTATIVE)
+			if ((ifa->flags & IFA_F_TENTATIVE) &&
+			    (!(ifa->flags & IFA_F_OPTIMISTIC)))
 				continue;
 			if (unlikely(score.addr_type == IPV6_ADDR_ANY ||
 				     score.addr_type & IPV6_ADDR_MULTICAST)) {
@@ -1027,15 +1035,17 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
 				}
 			}
 
-			/* Rule 3: Avoid deprecated address */
+			/* Rule 3: Avoid deprecated and optimistic address */
 			if (hiscore.rule < 3) {
 				if (ipv6_saddr_preferred(hiscore.addr_type) ||
-				    !(ifa_result->flags & IFA_F_DEPRECATED))
+				   (((ifa_result->flags &
+				    (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0)))
 					hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				hiscore.rule++;
 			}
 			if (ipv6_saddr_preferred(score.addr_type) ||
-			    !(ifa->flags & IFA_F_DEPRECATED)) {
+			   (((ifa_result->flags &
+			    (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0))) {
 				score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
 					score.rule = 3;
@@ -1174,7 +1184,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
 }
 
 
-int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
+int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, 
+		    unsigned char banned_flags)
 {
 	struct inet6_dev *idev;
 	int err = -EADDRNOTAVAIL;
@@ -1185,7 +1196,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
 
 		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 && !(ifp->flags & banned_flags)) {
 				ipv6_addr_copy(addr, &ifp->addr);
 				err = 0;
 				break;
@@ -1751,6 +1762,10 @@ ok:
 
 			update_lft = create = 1;
 			ifp->cstamp = jiffies;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+			if (ifp->idev->cnf.optimistic_dad)
+				ifp->flags |= IFA_F_OPTIMISTIC;
+#endif
 			addrconf_dad_start(ifp, RTF_ADDRCONF|RTF_PREFIX_RT);
 		}
 
@@ -1945,7 +1960,11 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
 		ifp->prefered_lft = prefered_lft;
 		ifp->tstamp = jiffies;
 		spin_unlock_bh(&ifp->lock);
-
+		/*
+		 * Note that section 3.1 of RFC 4429 indicates
+		 * That the Optimistic flag should not be set for
+		 * manually configured addresses
+		 */
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
 		addrconf_verify(0);
@@ -2123,7 +2142,8 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr
 {
 	struct inet6_ifaddr * ifp;
 
-	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
+	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, 
+		            IFA_F_PERMANENT|IFA_F_OPTIMISTIC);
 	if (!IS_ERR(ifp)) {
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
@@ -2190,7 +2210,7 @@ ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
 {
 	struct in6_addr lladdr;
 
-	if (!ipv6_get_lladdr(link_dev, &lladdr)) {
+	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
 		addrconf_add_linklocal(idev, &lladdr);
 		return 0;
 	}
@@ -2527,7 +2547,11 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp)
 	unsigned long rand_num;
 	struct inet6_dev *idev = ifp->idev;
 
-	rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		rand_num = 0;
+	else
+		rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+
 	ifp->probes = idev->cnf.dad_transmits;
 	addrconf_mod_timer(ifp, AC_DAD, rand_num);
 }
@@ -2553,7 +2577,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
 	    !(ifp->flags&IFA_F_TENTATIVE) ||
 	    ifp->flags & IFA_F_NODAD) {
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -2573,6 +2597,14 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 		addrconf_dad_stop(ifp);
 		return;
 	}
+
+	/*
+	 * Optimistic nodes need to join the anycast address
+	 * right away
+	 */
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addrconf_join_anycast(ifp);
+
 	addrconf_dad_kick(ifp);
 	spin_unlock_bh(&ifp->lock);
 out:
@@ -2597,7 +2629,7 @@ static void addrconf_dad_timer(unsigned long data)
 		 * DAD was successful
 		 */
 
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -2627,6 +2659,9 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
 	 *	Configure the address for reception. Now it is valid.
 	 */
 
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addrconf_leave_anycast(ifp);
+
 	ipv6_ifa_notify(RTM_NEWADDR, ifp);
 
 	/* If added prefix is link local and forwarding is off,
@@ -3398,6 +3433,9 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
 #endif
 #endif
 	array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad;
+#endif
 }
 
 static inline size_t inet6_if_nlmsg_size(void)
@@ -3917,6 +3955,17 @@ static struct addrconf_sysctl_table
 			.mode		=	0644,
 			.proc_handler	=	&proc_dointvec,
 		},
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+		{
+			.ctl_name	=	NET_IPV6_OPTIMISTIC_DAD,
+			.procname       =       "optimistic_dad",
+			.data           =       &ipv6_devconf.optimistic_dad,
+			.maxlen         =       sizeof(int),
+			.mode           =       0644,
+			.proc_handler   =       &proc_dointvec,
+
+		},
+#endif
 		{
 			.ctl_name	=	0,	/* sentinel */
 		}
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 7b7bd44..cf32b50 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -848,13 +848,43 @@ static int ip6_dst_lookup_tail(struct sock *sk,
 			       struct dst_entry **dst, struct flowi *fl)
 {
 	int err;
-
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	struct inet6_ifaddr *ifp;
+	struct flowi gateway_fl;
+#endif
 	if (*dst == NULL)
 		*dst = ip6_route_output(sk, fl);
 
 	if ((err = (*dst)->error))
 		goto out_err_release;
 
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	/*
+	 * Here if the dst entry we've looked up 
+	 * has a neighbour entry that is in the INCOMPLETE
+	 * state and the src address from the flow is 
+	 * marked as OPTIMISTIC, we release the found 
+	 * dst entry and replace it instead with the 
+	 * dst entry of the nexthop router
+	 */
+	if (((*dst)->neighbour->nud_state == NUD_INCOMPLETE) ||
+	    ((*dst)->neighbour->nud_state == NUD_FAILED)) {
+		ifp = ipv6_get_ifaddr(&fl->fl6_src, (*dst)->dev, 1);
+
+		if (ifp && ifp->flags & IFA_F_OPTIMISTIC) {
+			/*
+			 * We need to get the dst entry for the 
+			 * default router instead
+			 */
+			dst_release(*dst);
+			memcpy(&gateway_fl, fl, sizeof(struct flowi));
+			memset(&gateway_fl.fl6_dst, 0, sizeof(struct in6_addr));
+			*dst = ip6_route_output(sk, &gateway_fl);
+			if ((err = (*dst)->error))
+				goto out_err_release;				
+		}
+	}
+#endif
 	if (ipv6_addr_any(&fl->fl6_src)) {
 		err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src);
 		if (err)
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 882cde4..9c5273c 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
@@ -1789,7 +1789,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 6a9f616..90404d8 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -447,6 +447,8 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
 	ifp = ipv6_get_ifaddr(solicited_addr, dev, 1);
  	if (ifp) {
 		src_addr = solicited_addr;
+		if (ifp->flags & IFA_F_OPTIMISTIC)
+			override = 0;
 		in6_ifa_put(ifp);
 	} else {
 		if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr))
@@ -498,7 +500,8 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
         msg->icmph.icmp6_unused = 0;
         msg->icmph.icmp6_router    = router;
         msg->icmph.icmp6_solicited = solicited;
-        msg->icmph.icmp6_override  = override;
+	msg->icmph.icmp6_override  = override;
+
 
         /* Set the target address. */
 	ipv6_addr_copy(&msg->target, solicited_addr);
@@ -540,13 +543,16 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
         int len;
 	int err;
 	int send_llinfo;
+	unsigned char *lladdr = NULL;
 
 	if (saddr == NULL) {
-		if (ipv6_get_lladdr(dev, &addr_buf))
+		if (ipv6_get_lladdr(dev, &addr_buf, 
+				   (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
 			return;
 		saddr = &addr_buf;
 	}
 
+
 	ndisc_flow_init(&fl, NDISC_NEIGHBOUR_SOLICITATION, saddr, daddr,
 			dev->ifindex);
 
@@ -559,7 +565,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
 		return;
 
 	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
-	send_llinfo = dev->addr_len && !ipv6_addr_any(saddr);
+	send_llinfo = (dev->addr_len && !ipv6_addr_any(saddr)); 
+
 	if (send_llinfo)
 		len += ndisc_opt_addr_space(dev);
 
@@ -622,9 +629,21 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
         struct sk_buff *skb;
         struct icmp6hdr *hdr;
 	__u8 * opt;
+	struct inet6_ifaddr *ifp;
+	int include_sllao_option = 1;
         int len;
 	int err;
 
+	/*
+	 * Check the source address.  If its OPTIMISTIC
+	 * and addr_len is non-zero (implying the sllao option)
+	 * then don't send the RS (RFC 4429, section 2.2)
+	 */
+	ifp = ipv6_get_ifaddr(saddr, dev, 1);
+
+	if ((!ifp) || ((ifp->flags & IFA_F_OPTIMISTIC) && dev->addr_len))
+		include_sllao_option=0;
+
 	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
 			dev->ifindex);
 
@@ -664,7 +683,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
 
 	opt = (u8*) (hdr + 1);
 
-	if (dev->addr_len)
+	if (dev->addr_len && include_sllao_option)
 		ndisc_fill_addr_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr,
 				       dev->addr_len, dev->type);
 
@@ -746,6 +765,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	int dad = ipv6_addr_any(saddr);
 	int inc;
 	int is_router;
+	int type;
 
 	if (ipv6_addr_is_multicast(&msg->target)) {
 		ND_PRINTK2(KERN_WARNING 
@@ -796,28 +816,39 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	inc = ipv6_addr_is_multicast(daddr);
 
 	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
-		if (ifp->flags & IFA_F_TENTATIVE) {
-			/* Address is tentative. If the source
-			   is unspecified address, it is someone
-			   does DAD, otherwise we ignore solicitations
-			   until DAD timer expires.
-			 */
-			if (!dad)
+
+		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
+			if (dad) {
+				if (dev->type == ARPHRD_IEEE802_TR) {
+					unsigned char *sadr = skb->mac.raw;
+					if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
+					    sadr[9] == dev->dev_addr[1] &&
+					    sadr[10] == dev->dev_addr[2] &&
+					    sadr[11] == dev->dev_addr[3] &&
+					    sadr[12] == dev->dev_addr[4] &&
+					    sadr[13] == dev->dev_addr[5]) {
+						/* looped-back to us */
+						goto out;
+					}
+				}
+
+				/*
+				 * We are colliding with another node
+				 * who is doing DAD
+				 * so fail our DAD process
+				 */
+				addrconf_dad_failure(ifp);
 				goto out;
-			if (dev->type == ARPHRD_IEEE802_TR) {
-				unsigned char *sadr = skb->mac.raw;
-				if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
-				    sadr[9] == dev->dev_addr[1] &&
-				    sadr[10] == dev->dev_addr[2] &&
-				    sadr[11] == dev->dev_addr[3] &&
-				    sadr[12] == dev->dev_addr[4] &&
-				    sadr[13] == dev->dev_addr[5]) {
-					/* looped-back to us */
+			} else {
+				/*
+				 * This is not a dad solicitation.
+				 * If we are an optimistic node, 
+				 * we should respond.  
+				 * Otherwise, we should ignore it. 
+				 */
+				if (!(ifp->flags & IFA_F_OPTIMISTIC))
 					goto out;
-				}
 			}
-			addrconf_dad_failure(ifp); 
-			return;
 		}
 
 		idev = ifp->idev;
@@ -1406,7 +1437,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
 
 	dev = skb->dev;
 
-	if (ipv6_get_lladdr(dev, &saddr_buf)) {
+	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
 		ND_PRINTK2(KERN_WARNING
 			   "ICMPv6 Redirect: no link-local address on %s\n",
 			   dev->name);

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-02 19:06                                       ` Neil Horman
@ 2007-02-02 19:46                                         ` David Miller
  2007-02-02 20:13                                           ` Neil Horman
  2007-02-02 21:28                                         ` Brian Haley
  2007-02-02 21:50                                         ` Vlad Yasevich
  2 siblings, 1 reply; 71+ messages in thread
From: David Miller @ 2007-02-02 19:46 UTC (permalink / raw)
  To: nhorman
  Cc: yoshfuji, vladislav.yasevich, sri, kuznet, pekkas, jmorris,
	kaber, netdev

From: Neil Horman <nhorman@tuxdriver.com>
Date: Fri, 2 Feb 2007 14:06:34 -0500

> Ok, I'm still testing it, but heres a new patch for review.
> Significant changes include the addition of a
> CONFIG_IPV6_OPTIMISTIC_DAD option that is dependent on the inclusion
> of both IPPV6 and EXPERIMENTAL options, as well as a new method for
> redirecting packets from optimistic sources to incomplete neighbors
> by instead looking up a default router in ip6_dst_lookup_tail, as I
> described in my previous note.

This looks largely fine to me, but I wonder about one bit:

+
+	/*
+	 * Optimistic nodes need to join the anycast address
+	 * right away
+	 */
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addrconf_join_anycast(ifp);
+

If something can clear that bit during the DAD, we won't
call addrconf_leave_anycast() later.  Can that happen?

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-02 19:46                                         ` David Miller
@ 2007-02-02 20:13                                           ` Neil Horman
  2007-02-02 22:22                                             ` Vlad Yasevich
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-02-02 20:13 UTC (permalink / raw)
  To: David Miller
  Cc: yoshfuji, vladislav.yasevich, sri, kuznet, pekkas, jmorris,
	kaber, netdev

On Fri, Feb 02, 2007 at 11:46:08AM -0800, David Miller wrote:
> From: Neil Horman <nhorman@tuxdriver.com>
> Date: Fri, 2 Feb 2007 14:06:34 -0500
> 
> > Ok, I'm still testing it, but heres a new patch for review.
> > Significant changes include the addition of a
> > CONFIG_IPV6_OPTIMISTIC_DAD option that is dependent on the inclusion
> > of both IPPV6 and EXPERIMENTAL options, as well as a new method for
> > redirecting packets from optimistic sources to incomplete neighbors
> > by instead looking up a default router in ip6_dst_lookup_tail, as I
> > described in my previous note.
> 
> This looks largely fine to me, but I wonder about one bit:
> 
> +
> +	/*
> +	 * Optimistic nodes need to join the anycast address
> +	 * right away
> +	 */
> +	if (ifp->flags & IFA_F_OPTIMISTIC)
> +		addrconf_join_anycast(ifp);
> +
> 
> If something can clear that bit during the DAD, we won't
> call addrconf_leave_anycast() later.  Can that happen?
The only way the flag should get cleared once we've started DAD is when it
completes or fails.  In the failure case, we destroy the ifaddr structure, which
I think should force a leave_anycast, while the completed condition calls
addrconf_leave_anycast as part of the completion process before it clears the
flags, so I think we should be ok.  If anyone sees anything to the contrary,
please let me know and I'll be sure to  plug the hole.

Thanks & Regards
Neil

> -
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-02 19:06                                       ` Neil Horman
  2007-02-02 19:46                                         ` David Miller
@ 2007-02-02 21:28                                         ` Brian Haley
  2007-02-02 22:05                                           ` Vlad Yasevich
  2007-02-02 21:50                                         ` Vlad Yasevich
  2 siblings, 1 reply; 71+ messages in thread
From: Brian Haley @ 2007-02-02 21:28 UTC (permalink / raw)
  To: Neil Horman
  Cc: YOSHIFUJI Hideaki / ????,
	vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber,
	netdev

Hi Neil,

> @@ -830,7 +836,8 @@ retry:
>  	ift = !max_addresses ||
>  	      ipv6_count_addresses(idev) < max_addresses ? 
>  		ipv6_add_addr(idev, &addr, tmp_plen,
> -			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
> +			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
> +			      IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;

So why are you always adding these as optimistic now?  Shouldn't this be 
triggering off idev->cnf.optimistic_dad?  I know you're clearing it in 
ipv6_add_addr(), but I liked Vlad's suggestion of not setting it 
initially since this way seems backwards.

> @@ -2123,7 +2142,8 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr
>  {
>  	struct inet6_ifaddr * ifp;
>  
> -	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
> +	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, 
> +		            IFA_F_PERMANENT|IFA_F_OPTIMISTIC);

Here too.

-Brian

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-02 19:06                                       ` Neil Horman
  2007-02-02 19:46                                         ` David Miller
  2007-02-02 21:28                                         ` Brian Haley
@ 2007-02-02 21:50                                         ` Vlad Yasevich
  2007-02-03 15:03                                           ` Neil Horman
  2 siblings, 1 reply; 71+ messages in thread
From: Vlad Yasevich @ 2007-02-02 21:50 UTC (permalink / raw)
  To: Neil Horman
  Cc: YOSHIFUJI Hideaki / 吉藤英明,
	sri, davem, kuznet, pekkas, jmorris, kaber, netdev

Neil Horman wrote:
> Ok, I'm still testing it, but heres a new patch for review.  Significant changes
> include the addition of a CONFIG_IPV6_OPTIMISTIC_DAD option that is dependent on
> the inclusion of both IPPV6 and EXPERIMENTAL options, as well as a new method
> for redirecting packets from optimistic sources to incomplete neighbors by
> instead looking up a default router in ip6_dst_lookup_tail, as I described in my
> previous note.
> 

Hi Neil

Still digesting the routing changes, but I think you missed addrconf_prefix_rcv()... ;)


-vlad

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-02 21:28                                         ` Brian Haley
@ 2007-02-02 22:05                                           ` Vlad Yasevich
  2007-02-02 23:57                                             ` Brian Haley
  0 siblings, 1 reply; 71+ messages in thread
From: Vlad Yasevich @ 2007-02-02 22:05 UTC (permalink / raw)
  To: Brian Haley
  Cc: Neil Horman, YOSHIFUJI Hideaki / ????,
	sri, davem, kuznet, pekkas, jmorris, kaber, netdev

Brian Haley wrote:
> Hi Neil,
> 
>> @@ -830,7 +836,8 @@ retry:
>>      ift = !max_addresses ||
>>            ipv6_count_addresses(idev) < max_addresses ?         
>> ipv6_add_addr(idev, &addr, tmp_plen,
>> -                  ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK,
>> IFA_F_TEMPORARY) : NULL;
>> +                  ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK,
>> +                  IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
> 

Hi Brian

> So why are you always adding these as optimistic now?  Shouldn't this be
> triggering off idev->cnf.optimistic_dad?  I know you're clearing it in
> ipv6_add_addr(), but I liked Vlad's suggestion of not setting it
> initially since this way seems backwards.

The troubling case seems to manually configured addresses (inet6_addr_add()).
If we can clearly and easily distinguish between this case of address
and all the other ones, then we can simply set the flag in ipv6_add_addr, like
we set the tentative flag.

So, we can introduce another parameter to ipv6_add_addr() or another flag that
can distinguish manual config.  Otherwise, we can keep the code as is, passing
the optimistic flag from needed callers, and clearing it inside ipv6_add_addr().

My thought was to clear it from the 'flags' parameter before ifa->flags was
set, but that  doesn't really matter.

-vlad

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-02 20:13                                           ` Neil Horman
@ 2007-02-02 22:22                                             ` Vlad Yasevich
  2007-02-03 15:06                                               ` Neil Horman
  0 siblings, 1 reply; 71+ messages in thread
From: Vlad Yasevich @ 2007-02-02 22:22 UTC (permalink / raw)
  To: Neil Horman
  Cc: David Miller, yoshfuji, sri, kuznet, pekkas, jmorris, kaber, netdev

Neil Horman wrote:
> On Fri, Feb 02, 2007 at 11:46:08AM -0800, David Miller wrote:
>> From: Neil Horman <nhorman@tuxdriver.com>
>> Date: Fri, 2 Feb 2007 14:06:34 -0500
>>
>>> Ok, I'm still testing it, but heres a new patch for review.
>>> Significant changes include the addition of a
>>> CONFIG_IPV6_OPTIMISTIC_DAD option that is dependent on the inclusion
>>> of both IPPV6 and EXPERIMENTAL options, as well as a new method for
>>> redirecting packets from optimistic sources to incomplete neighbors
>>> by instead looking up a default router in ip6_dst_lookup_tail, as I
>>> described in my previous note.
>> This looks largely fine to me, but I wonder about one bit:
>>
>> +
>> +	/*
>> +	 * Optimistic nodes need to join the anycast address
>> +	 * right away
>> +	 */
>> +	if (ifp->flags & IFA_F_OPTIMISTIC)
>> +		addrconf_join_anycast(ifp);
>> +
>>
>> If something can clear that bit during the DAD, we won't
>> call addrconf_leave_anycast() later.  Can that happen?
> The only way the flag should get cleared once we've started DAD is when it
> completes or fails.  In the failure case, we destroy the ifaddr structure, which
> I think should force a leave_anycast, while the completed condition calls
> addrconf_leave_anycast as part of the completion process before it clears the
> flags, so I think we should be ok.  If anyone sees anything to the contrary,
> please let me know and I'll be sure to  plug the hole.

I think there is a hole:

Looking for addrconf_leave_anycast() shows:
dev_forward_change():	475 addrconf_leave_anycast(ifa);
__ipv6_ifa_notify():	3613 addrconf_leave_anycast(ifp);

Problem:  __ipv6_ifa_notify only performs the 'leave' if forwarding is enabled.
	  However, OPTIMISTIC, is set when forwarding is _disabled_.


-vlad

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-02 22:05                                           ` Vlad Yasevich
@ 2007-02-02 23:57                                             ` Brian Haley
  2007-02-03 15:05                                               ` Neil Horman
  0 siblings, 1 reply; 71+ messages in thread
From: Brian Haley @ 2007-02-02 23:57 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: Neil Horman, YOSHIFUJI Hideaki / ????,
	sri, davem, kuznet, pekkas, jmorris, kaber, netdev

Hi Vlad,

Vlad Yasevich wrote:
> Brian Haley wrote:
>> Hi Neil,
>>
>>> @@ -830,7 +836,8 @@ retry:
>>>      ift = !max_addresses ||
>>>            ipv6_count_addresses(idev) < max_addresses ?         
>>> ipv6_add_addr(idev, &addr, tmp_plen,
>>> -                  ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK,
>>> IFA_F_TEMPORARY) : NULL;
>>> +                  ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK,
>>> +                  IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
> 
> Hi Brian
> 
>> So why are you always adding these as optimistic now?  Shouldn't this be
>> triggering off idev->cnf.optimistic_dad?  I know you're clearing it in
>> ipv6_add_addr(), but I liked Vlad's suggestion of not setting it
>> initially since this way seems backwards.
> 
> The troubling case seems to manually configured addresses (inet6_addr_add()).
> If we can clearly and easily distinguish between this case of address
> and all the other ones, then we can simply set the flag in ipv6_add_addr, like
> we set the tentative flag.

Right, I guess maybe I'm trying to figure out what 
idev->cnf.optimistic_dad means:

1. Interface supports OPTIMISTIC addresses
2. All auto-configured addresses on interface are OPTIMISTIC
3. ???

All other addresses are created w/out OPTIMISTIC set.

I think manually-configured addresses can be tagged as OPTIMISTIC just 
like MIPv6 Home Addresses are if we just change this line in 
inet6_rtm_newaddr():

< ifa_flags = ifm->ifa_flags & (IFA_F_NODAD | IFA_F_HOMEADDRESS);
--
 > ifa_flags = ifm->ifa_flags & (IFA_F_NODAD | IFA_F_HOMEADDRESS | 
IFA_F_OPTIMISTIC);

and tweak the rest of the code, but that doesn't cover the 
addrconf_add_ifaddr() codepath via ioctl(SIOCSIFADDR).

I can generate a patch based-on Neil's, but it will take me until Monday 
to get it out.

-Brian

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-02 21:50                                         ` Vlad Yasevich
@ 2007-02-03 15:03                                           ` Neil Horman
       [not found]                                             ` <20070205205651.GB484@hmsreliant.homelinux.net>
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-02-03 15:03 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: YOSHIFUJI Hideaki / 吉藤英明,
	sri, davem, kuznet, pekkas, jmorris, kaber, netdev

On Fri, Feb 02, 2007 at 04:50:35PM -0500, Vlad Yasevich wrote:
> Neil Horman wrote:
> > Ok, I'm still testing it, but heres a new patch for review.  Significant changes
> > include the addition of a CONFIG_IPV6_OPTIMISTIC_DAD option that is dependent on
> > the inclusion of both IPPV6 and EXPERIMENTAL options, as well as a new method
> > for redirecting packets from optimistic sources to incomplete neighbors by
> > instead looking up a default router in ip6_dst_lookup_tail, as I described in my
> > previous note.
> > 
> 
> Hi Neil
> 
> Still digesting the routing changes, but I think you missed addrconf_prefix_rcv()... ;)
> 
I may well have, I'll take a good look on monday.  Thanks for the eyes :)
Neil

> 
> -vlad

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-02 23:57                                             ` Brian Haley
@ 2007-02-03 15:05                                               ` Neil Horman
  2007-02-05 17:33                                                 ` Brian Haley
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-02-03 15:05 UTC (permalink / raw)
  To: Brian Haley
  Cc: Vlad Yasevich, YOSHIFUJI Hideaki / ????,
	sri, davem, kuznet, pekkas, jmorris, kaber, netdev

On Fri, Feb 02, 2007 at 06:57:37PM -0500, Brian Haley wrote:
> Hi Vlad,
> 
> Vlad Yasevich wrote:
> >Brian Haley wrote:
> >>Hi Neil,
> >>
> >>>@@ -830,7 +836,8 @@ retry:
> >>>     ift = !max_addresses ||
> >>>           ipv6_count_addresses(idev) < max_addresses ?         
> >>>ipv6_add_addr(idev, &addr, tmp_plen,
> >>>-                  ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK,
> >>>IFA_F_TEMPORARY) : NULL;
> >>>+                  ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK,
> >>>+                  IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
> >
> >Hi Brian
> >
> >>So why are you always adding these as optimistic now?  Shouldn't this be
> >>triggering off idev->cnf.optimistic_dad?  I know you're clearing it in
> >>ipv6_add_addr(), but I liked Vlad's suggestion of not setting it
> >>initially since this way seems backwards.
> >
> >The troubling case seems to manually configured addresses 
> >(inet6_addr_add()).
> >If we can clearly and easily distinguish between this case of address
> >and all the other ones, then we can simply set the flag in ipv6_add_addr, 
> >like
> >we set the tentative flag.
> 
> Right, I guess maybe I'm trying to figure out what 
> idev->cnf.optimistic_dad means:
> 
> 1. Interface supports OPTIMISTIC addresses
> 2. All auto-configured addresses on interface are OPTIMISTIC
> 3. ???
> 
The flag means that the interface supports optimistic DAD for all addresses
which are eligible to take advantage of RFC 4429.  As Vlad mentioned manually
configured addresses are excluded from that set.

> All other addresses are created w/out OPTIMISTIC set.
> 
> I think manually-configured addresses can be tagged as OPTIMISTIC just 
> like MIPv6 Home Addresses are if we just change this line in 
> inet6_rtm_newaddr():
> 
> < ifa_flags = ifm->ifa_flags & (IFA_F_NODAD | IFA_F_HOMEADDRESS);
> --
> > ifa_flags = ifm->ifa_flags & (IFA_F_NODAD | IFA_F_HOMEADDRESS | 
> IFA_F_OPTIMISTIC);
> 
> and tweak the rest of the code, but that doesn't cover the 
> addrconf_add_ifaddr() codepath via ioctl(SIOCSIFADDR).
> 
> I can generate a patch based-on Neil's, but it will take me until Monday 
> to get it out.
> 
Please, if you think you can find a way for us to do optimistic dad flags as
opt-in, rather than masked out, I'm all for it.  Thanks!
Neil

> -Brian

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-02 22:22                                             ` Vlad Yasevich
@ 2007-02-03 15:06                                               ` Neil Horman
  0 siblings, 0 replies; 71+ messages in thread
From: Neil Horman @ 2007-02-03 15:06 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: David Miller, yoshfuji, sri, kuznet, pekkas, jmorris, kaber, netdev

On Fri, Feb 02, 2007 at 05:22:31PM -0500, Vlad Yasevich wrote:
> Neil Horman wrote:
> > On Fri, Feb 02, 2007 at 11:46:08AM -0800, David Miller wrote:
> >> From: Neil Horman <nhorman@tuxdriver.com>
> >> Date: Fri, 2 Feb 2007 14:06:34 -0500
> >>
> >>> Ok, I'm still testing it, but heres a new patch for review.
> >>> Significant changes include the addition of a
> >>> CONFIG_IPV6_OPTIMISTIC_DAD option that is dependent on the inclusion
> >>> of both IPPV6 and EXPERIMENTAL options, as well as a new method for
> >>> redirecting packets from optimistic sources to incomplete neighbors
> >>> by instead looking up a default router in ip6_dst_lookup_tail, as I
> >>> described in my previous note.
> >> This looks largely fine to me, but I wonder about one bit:
> >>
> >> +
> >> +	/*
> >> +	 * Optimistic nodes need to join the anycast address
> >> +	 * right away
> >> +	 */
> >> +	if (ifp->flags & IFA_F_OPTIMISTIC)
> >> +		addrconf_join_anycast(ifp);
> >> +
> >>
> >> If something can clear that bit during the DAD, we won't
> >> call addrconf_leave_anycast() later.  Can that happen?
> > The only way the flag should get cleared once we've started DAD is when it
> > completes or fails.  In the failure case, we destroy the ifaddr structure, which
> > I think should force a leave_anycast, while the completed condition calls
> > addrconf_leave_anycast as part of the completion process before it clears the
> > flags, so I think we should be ok.  If anyone sees anything to the contrary,
> > please let me know and I'll be sure to  plug the hole.
> 
> I think there is a hole:
> 
> Looking for addrconf_leave_anycast() shows:
> dev_forward_change():	475 addrconf_leave_anycast(ifa);
> __ipv6_ifa_notify():	3613 addrconf_leave_anycast(ifp);
> 
> Problem:  __ipv6_ifa_notify only performs the 'leave' if forwarding is enabled.
> 	  However, OPTIMISTIC, is set when forwarding is _disabled_.
> 
> 
> -vlad
I think you may be right.  I'll fix that up monday AM.  Thanks!
Neil


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-03 15:05                                               ` Neil Horman
@ 2007-02-05 17:33                                                 ` Brian Haley
  2007-02-05 18:37                                                   ` Neil Horman
  0 siblings, 1 reply; 71+ messages in thread
From: Brian Haley @ 2007-02-05 17:33 UTC (permalink / raw)
  To: Neil Horman
  Cc: Vlad Yasevich, YOSHIFUJI Hideaki / ????,
	sri, davem, kuznet, pekkas, jmorris, kaber, netdev

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

> Please, if you think you can find a way for us to do optimistic dad flags as
> opt-in, rather than masked out, I'm all for it.  Thanks!

This patch should apply on-top of yours, if you want I can send the 
whole thing out too.  I've only compile-tested it, so don't know if it 
behaves the same as your original.

-Brian


Signed-off-by: Brian Haley <brian.haley@hp.com>

[-- Attachment #2: opti.patch --]
[-- Type: text/x-patch, Size: 2930 bytes --]

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index c341371..ddac8b0 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -593,13 +593,8 @@ ipv6_add_addr(struct inet6_dev *idev, co
 	ifa->cstamp = ifa->tstamp = jiffies;
 
 	ifa->rt = rt;
-#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
-	if (!idev->cnf.optimistic_dad || ipv6_devconf.forwarding ||
-	   (ifa->rt->rt6i_nexthop == NULL))
+	if (rt->rt6i_nexthop == NULL)
 		ifa->flags &= ~IFA_F_OPTIMISTIC;
-#else
-	ifa->flags &= ~IFA_F_OPTIMISTIC;
-#endif
 	ifa->idev = idev;
 	in6_dev_hold(idev);
 	/* For caller */
@@ -776,6 +771,7 @@ static int ipv6_create_tempaddr(struct i
 	int tmp_plen;
 	int ret = 0;
 	int max_addresses;
+	u32 addr_flags;
 
 	write_lock(&idev->lock);
 	if (ift) {
@@ -833,11 +829,17 @@ retry:
 	spin_unlock_bh(&ifp->lock);
 
 	write_unlock(&idev->lock);
+
+	addr_flags = IFA_F_TEMPORARY;
+	/* set in addrconf_prefix_rcv() */
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addr_flags |= IFA_F_OPTIMISTIC;
+
 	ift = !max_addresses ||
 	      ipv6_count_addresses(idev) < max_addresses ? 
 		ipv6_add_addr(idev, &addr, tmp_plen,
 			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
-			      IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
+			      addr_flags) : NULL;
 	if (!ift || IS_ERR(ift)) {
 		in6_ifa_put(ifp);
 		in6_dev_put(idev);
@@ -1746,6 +1748,13 @@ ok:
 
 		if (ifp == NULL && 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 &&
+			    !ipv6_devconf.forwarding)
+				addr_flags = IFA_F_OPTIMISTIC;
+#endif
 
 			/* Do not allow to create too much of autoconfigured
 			 * addresses; this would be too easy way to crash kernel.
@@ -1753,7 +1762,8 @@ ok:
 			if (!max_addresses ||
 			    ipv6_count_addresses(in6_dev) < max_addresses)
 				ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len,
-						    addr_type&IPV6_ADDR_SCOPE_MASK, 0);
+						    addr_type&IPV6_ADDR_SCOPE_MASK,
+						    addr_flags);
 
 			if (!ifp || IS_ERR(ifp)) {
 				in6_dev_put(in6_dev);
@@ -1762,10 +1772,6 @@ ok:
 
 			update_lft = create = 1;
 			ifp->cstamp = jiffies;
-#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
-			if (ifp->idev->cnf.optimistic_dad)
-				ifp->flags |= IFA_F_OPTIMISTIC;
-#endif
 			addrconf_dad_start(ifp, RTF_ADDRCONF|RTF_PREFIX_RT);
 		}
 
@@ -2141,9 +2147,16 @@ static void init_loopback(struct net_dev
 static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr)
 {
 	struct inet6_ifaddr * ifp;
+	u32 addr_flags = IFA_F_PERMANENT;
+
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	if (idev->cnf.optimistic_dad &&
+	    !ipv6_devconf.forwarding)
+		addr_flags |= IFA_F_OPTIMISTIC;
+#endif
+
 
-	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, 
-		            IFA_F_PERMANENT|IFA_F_OPTIMISTIC);
+	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, addr_flags);
 	if (!IS_ERR(ifp)) {
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-05 17:33                                                 ` Brian Haley
@ 2007-02-05 18:37                                                   ` Neil Horman
  0 siblings, 0 replies; 71+ messages in thread
From: Neil Horman @ 2007-02-05 18:37 UTC (permalink / raw)
  To: Brian Haley
  Cc: Vlad Yasevich, YOSHIFUJI Hideaki / ????,
	sri, davem, kuznet, pekkas, jmorris, kaber, netdev

On Mon, Feb 05, 2007 at 12:33:55PM -0500, Brian Haley wrote:
> >Please, if you think you can find a way for us to do optimistic dad flags 
> >as
> >opt-in, rather than masked out, I'm all for it.  Thanks!
> 
> This patch should apply on-top of yours, if you want I can send the 
> whole thing out too.  I've only compile-tested it, so don't know if it 
> behaves the same as your original.
> 
> -Brian
> 
Thank you brian, I'll look over and incorporate this into my new tree as I'm
fixing up the anycast hole Vlad brought up

Thanks & Regards
Neil

> 
> Signed-off-by: Brian Haley <brian.haley@hp.com>

> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index c341371..ddac8b0 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -593,13 +593,8 @@ ipv6_add_addr(struct inet6_dev *idev, co
>  	ifa->cstamp = ifa->tstamp = jiffies;
>  
>  	ifa->rt = rt;
> -#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> -	if (!idev->cnf.optimistic_dad || ipv6_devconf.forwarding ||
> -	   (ifa->rt->rt6i_nexthop == NULL))
> +	if (rt->rt6i_nexthop == NULL)
>  		ifa->flags &= ~IFA_F_OPTIMISTIC;
> -#else
> -	ifa->flags &= ~IFA_F_OPTIMISTIC;
> -#endif
>  	ifa->idev = idev;
>  	in6_dev_hold(idev);
>  	/* For caller */
> @@ -776,6 +771,7 @@ static int ipv6_create_tempaddr(struct i
>  	int tmp_plen;
>  	int ret = 0;
>  	int max_addresses;
> +	u32 addr_flags;
>  
>  	write_lock(&idev->lock);
>  	if (ift) {
> @@ -833,11 +829,17 @@ retry:
>  	spin_unlock_bh(&ifp->lock);
>  
>  	write_unlock(&idev->lock);
> +
> +	addr_flags = IFA_F_TEMPORARY;
> +	/* set in addrconf_prefix_rcv() */
> +	if (ifp->flags & IFA_F_OPTIMISTIC)
> +		addr_flags |= IFA_F_OPTIMISTIC;
> +
>  	ift = !max_addresses ||
>  	      ipv6_count_addresses(idev) < max_addresses ? 
>  		ipv6_add_addr(idev, &addr, tmp_plen,
>  			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
> -			      IFA_F_TEMPORARY|IFA_F_OPTIMISTIC) : NULL;
> +			      addr_flags) : NULL;
>  	if (!ift || IS_ERR(ift)) {
>  		in6_ifa_put(ifp);
>  		in6_dev_put(idev);
> @@ -1746,6 +1748,13 @@ ok:
>  
>  		if (ifp == NULL && 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 &&
> +			    !ipv6_devconf.forwarding)
> +				addr_flags = IFA_F_OPTIMISTIC;
> +#endif
>  
>  			/* Do not allow to create too much of autoconfigured
>  			 * addresses; this would be too easy way to crash kernel.
> @@ -1753,7 +1762,8 @@ ok:
>  			if (!max_addresses ||
>  			    ipv6_count_addresses(in6_dev) < max_addresses)
>  				ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len,
> -						    addr_type&IPV6_ADDR_SCOPE_MASK, 0);
> +						    addr_type&IPV6_ADDR_SCOPE_MASK,
> +						    addr_flags);
>  
>  			if (!ifp || IS_ERR(ifp)) {
>  				in6_dev_put(in6_dev);
> @@ -1762,10 +1772,6 @@ ok:
>  
>  			update_lft = create = 1;
>  			ifp->cstamp = jiffies;
> -#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> -			if (ifp->idev->cnf.optimistic_dad)
> -				ifp->flags |= IFA_F_OPTIMISTIC;
> -#endif
>  			addrconf_dad_start(ifp, RTF_ADDRCONF|RTF_PREFIX_RT);
>  		}
>  
> @@ -2141,9 +2147,16 @@ static void init_loopback(struct net_dev
>  static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr)
>  {
>  	struct inet6_ifaddr * ifp;
> +	u32 addr_flags = IFA_F_PERMANENT;
> +
> +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> +	if (idev->cnf.optimistic_dad &&
> +	    !ipv6_devconf.forwarding)
> +		addr_flags |= IFA_F_OPTIMISTIC;
> +#endif
> +
>  
> -	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, 
> -		            IFA_F_PERMANENT|IFA_F_OPTIMISTIC);
> +	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, addr_flags);
>  	if (!IS_ERR(ifp)) {
>  		addrconf_dad_start(ifp, 0);
>  		in6_ifa_put(ifp);


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
       [not found]                                             ` <20070205205651.GB484@hmsreliant.homelinux.net>
@ 2007-02-06  1:24                                               ` YOSHIFUJI Hideaki / 吉藤英明
  2007-02-06  1:32                                                 ` David Miller
  2007-02-06 12:51                                                 ` Neil Horman
  0 siblings, 2 replies; 71+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2007-02-06  1:24 UTC (permalink / raw)
  To: nhorman
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber,
	netdev, yoshfuji

Hello.

In article <20070205205651.GB484@hmsreliant.homelinux.net> (at Mon, 5 Feb 2007 15:56:51 -0500), Neil Horman <nhorman@tuxdriver.com> says:


>  		if (ifp == NULL && 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 &&
> +			    !ipv6_devconf.forwarding)
> +				addr_flags = IFA_F_OPTIMISTIC;
> +#endif
>  
>  			/* Do not allow to create too much of autoconfigured
>  			 * addresses; this would be too easy way to crash kernel.
> @@ -1742,7 +1762,8 @@ ok:
>  			if (!max_addresses ||
>  			    ipv6_count_addresses(in6_dev) < max_addresses)
>  				ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len,
> -						    addr_type&IPV6_ADDR_SCOPE_MASK, 0);
> +						    addr_type&IPV6_ADDR_SCOPE_MASK,
> +						    addr_flags);
>  
>  			if (!ifp || IS_ERR(ifp)) {
>  				in6_dev_put(in6_dev);
> @@ -1945,7 +1966,11 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
>  		ifp->prefered_lft = prefered_lft;
>  		ifp->tstamp = jiffies;
>  		spin_unlock_bh(&ifp->lock);
> -
> +		/*
> +		 * Note that section 3.1 of RFC 4429 indicates
> +		 * That the Optimistic flag should not be set for
> +		 * manually configured addresses
> +		 */
>  		addrconf_dad_start(ifp, 0);
>  		in6_ifa_put(ifp);
>  		addrconf_verify(0);
> @@ -2122,8 +2147,16 @@ static void init_loopback(struct net_device *dev)
>  static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr)
>  {
>  	struct inet6_ifaddr * ifp;
> +	u32 addr_flags = IFA_F_PERMANENT;
> +
> +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> +	if (idev->cnf.optimistic_dad &&
> +	    !ipv6_devconf.forwarding)
> +		addr_flags |= IFA_F_OPTIMISTIC;
> +#endif
> +
>  
> -	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
> +	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, addr_flags);
>  	if (!IS_ERR(ifp)) {
>  		addrconf_dad_start(ifp, 0);
>  		in6_ifa_put(ifp);
> @@ -2190,7 +2223,7 @@ ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
>  {
>  	struct in6_addr lladdr;
>  
> -	if (!ipv6_get_lladdr(link_dev, &lladdr)) {
> +	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
>  		addrconf_add_linklocal(idev, &lladdr);
>  		return 0;
>  	}
> @@ -2527,7 +2560,11 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp)
>  	unsigned long rand_num;
>  	struct inet6_dev *idev = ifp->idev;
>  
> -	rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
> +	if (ifp->flags & IFA_F_OPTIMISTIC)
> +		rand_num = 0;
> +	else
> +		rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
> +
>  	ifp->probes = idev->cnf.dad_transmits;
>  	addrconf_mod_timer(ifp, AC_DAD, rand_num);
>  }
> @@ -2553,7 +2590,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
>  	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
>  	    !(ifp->flags&IFA_F_TENTATIVE) ||
>  	    ifp->flags & IFA_F_NODAD) {
> -		ifp->flags &= ~IFA_F_TENTATIVE;
> +		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
>  		spin_unlock_bh(&ifp->lock);
>  		read_unlock_bh(&idev->lock);
>  
> @@ -2573,6 +2610,14 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
>  		addrconf_dad_stop(ifp);
>  		return;
>  	}
> +
> +	/*
> +	 * Optimistic nodes need to join the anycast address
> +	 * right away
> +	 */
> +	if (ifp->flags & IFA_F_OPTIMISTIC)
> +		addrconf_join_anycast(ifp);
> +
>  	addrconf_dad_kick(ifp);
>  	spin_unlock_bh(&ifp->lock);
>  out:
> @@ -2597,7 +2642,7 @@ static void addrconf_dad_timer(unsigned long data)
>  		 * DAD was successful
>  		 */
>  
> -		ifp->flags &= ~IFA_F_TENTATIVE;
> +		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
>  		spin_unlock_bh(&ifp->lock);
>  		read_unlock_bh(&idev->lock);
>  
> @@ -2627,6 +2672,9 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
>  	 *	Configure the address for reception. Now it is valid.
>  	 */
>  
> +	if (ifp->flags & IFA_F_OPTIMISTIC)
> +		addrconf_leave_anycast(ifp);
> +
>  	ipv6_ifa_notify(RTM_NEWADDR, ifp);
>  
>  	/* If added prefix is link local and forwarding is off,
> @@ -3398,6 +3446,9 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
>  #endif
>  #endif
>  	array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp;
> +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> +	array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad;
> +#endif
>  }
>  
>  static inline size_t inet6_if_nlmsg_size(void)
> @@ -3605,11 +3656,18 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
>  	switch (event) {
>  	case RTM_NEWADDR:
>  		ip6_ins_rt(ifp->rt);
> -		if (ifp->idev->cnf.forwarding)
> +		/*
> +		 * Note that optimistic addresses
> +		 * join anycast at the start of DAD, not here
> +		 * like non-optimistic addresses
> +		 */
> +		if ((ifp->idev->cnf.forwarding) &&
> +		    (!(ifp->flags & IFA_F_OPTIMISTIC)))
>  			addrconf_join_anycast(ifp);
>  		break;
>  	case RTM_DELADDR:
> -		if (ifp->idev->cnf.forwarding)
> +		if ((ifp->idev->cnf.forwarding) ||
> +		    (ifp->flags & IFA_F_OPTIMISTIC))
>  			addrconf_leave_anycast(ifp);
>  		addrconf_leave_solict(ifp->idev, &ifp->addr);
>  		dst_hold(&ifp->rt->u.dst);
> @@ -3917,6 +3975,17 @@ static struct addrconf_sysctl_table
>  			.mode		=	0644,
>  			.proc_handler	=	&proc_dointvec,
>  		},
> +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> +		{
> +			.ctl_name	=	NET_IPV6_OPTIMISTIC_DAD,
> +			.procname       =       "optimistic_dad",
> +			.data           =       &ipv6_devconf.optimistic_dad,
> +			.maxlen         =       sizeof(int),
> +			.mode           =       0644,
> +			.proc_handler   =       &proc_dointvec,
> +
> +		},
> +#endif
>  		{
>  			.ctl_name	=	0,	/* sentinel */
>  		}
> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
> index 7b7bd44..cf32b50 100644
> --- a/net/ipv6/ip6_output.c
> +++ b/net/ipv6/ip6_output.c
> @@ -848,13 +848,43 @@ static int ip6_dst_lookup_tail(struct sock *sk,
>  			       struct dst_entry **dst, struct flowi *fl)
>  {
>  	int err;
> -
> +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> +	struct inet6_ifaddr *ifp;
> +	struct flowi gateway_fl;
> +#endif
>  	if (*dst == NULL)
>  		*dst = ip6_route_output(sk, fl);
>  
>  	if ((err = (*dst)->error))
>  		goto out_err_release;
>  
> +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> +	/*
> +	 * Here if the dst entry we've looked up 
> +	 * has a neighbour entry that is in the INCOMPLETE
> +	 * state and the src address from the flow is 
> +	 * marked as OPTIMISTIC, we release the found 
> +	 * dst entry and replace it instead with the 
> +	 * dst entry of the nexthop router
> +	 */
> +	if (((*dst)->neighbour->nud_state == NUD_INCOMPLETE) ||
> +	    ((*dst)->neighbour->nud_state == NUD_FAILED)) {
> +		ifp = ipv6_get_ifaddr(&fl->fl6_src, (*dst)->dev, 1);
> +
> +		if (ifp && ifp->flags & IFA_F_OPTIMISTIC) {
> +			/*
> +			 * We need to get the dst entry for the 
> +			 * default router instead
> +			 */
> +			dst_release(*dst);
> +			memcpy(&gateway_fl, fl, sizeof(struct flowi));
> +			memset(&gateway_fl.fl6_dst, 0, sizeof(struct in6_addr));
> +			*dst = ip6_route_output(sk, &gateway_fl);
> +			if ((err = (*dst)->error))
> +				goto out_err_release;				
> +		}
> +	}
> +#endif
>  	if (ipv6_addr_any(&fl->fl6_src)) {
>  		err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src);
>  		if (err)

This is not correct.
Please put that "ifdef" after source address selection
(just after the "if" statement just after this "#endif")
and check IFA_F_OPTIMISTIC of source address.

You can say,
  !((*dst)->neighbour->nud_state & NUD_VALID)
instead of 
  ((*dst)->neighbour->nud_state == NUD_INCOMPLETE) ||
  ((*dst)->neighbour->nud_state == NUD_FAILED)
.

> @@ -498,7 +500,8 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
>          msg->icmph.icmp6_unused = 0;
>          msg->icmph.icmp6_router    = router;
>          msg->icmph.icmp6_solicited = solicited;
> -        msg->icmph.icmp6_override  = override;
> +	msg->icmph.icmp6_override  = override;
> +
>  
>          /* Set the target address. */
>  	ipv6_addr_copy(&msg->target, solicited_addr);

Why do you "change" this?

> @@ -540,13 +543,16 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
:
>  			return;
>  		saddr = &addr_buf;
>  	}
>  
> +
>  	ndisc_flow_init(&fl, NDISC_NEIGHBOUR_SOLICITATION, saddr, daddr,
>  			dev->ifindex);
>  

ditto

> @@ -559,7 +565,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
>  		return;
>  
>  	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
> -	send_llinfo = dev->addr_len && !ipv6_addr_any(saddr);
> +	send_llinfo = (dev->addr_len && !ipv6_addr_any(saddr)); 
> +
>  	if (send_llinfo)
>  		len += ndisc_opt_addr_space(dev);
>  

ditto.

> @@ -622,9 +629,21 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
>          struct sk_buff *skb;
>          struct icmp6hdr *hdr;
>  	__u8 * opt;
> +	struct inet6_ifaddr *ifp;
> +	int include_sllao_option = 1;

int send_sllao = 1;

>          int len;
>  	int err;
>  
> +	/*
> +	 * Check the source address.  If its OPTIMISTIC
> +	 * and addr_len is non-zero (implying the sllao option)
> +	 * then don't send the RS (RFC 4429, section 2.2)
> +	 */
> +	ifp = ipv6_get_ifaddr(saddr, dev, 1);
> +
> +	if ((!ifp) || ((ifp->flags & IFA_F_OPTIMISTIC) && dev->addr_len))
> +		include_sllao_option=0;
> +
>  	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
>  			dev->ifindex);
>  

if (!dev->addr_len || !ifp || ifp->IFA_F_OPTIMISTIC)
        send_sllao = 0;

> @@ -664,7 +683,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
>  
>  	opt = (u8*) (hdr + 1);
>  
> -	if (dev->addr_len)
> +	if (dev->addr_len && include_sllao_option)
>  		ndisc_fill_addr_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr,
>  				       dev->addr_len, dev->type);
>  

if (send_sllao)
	ndisc_fill_addr_option(...)

Please adjust allocation space as well.

--yoshfuji

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-06  1:24                                               ` YOSHIFUJI Hideaki / 吉藤英明
@ 2007-02-06  1:32                                                 ` David Miller
  2007-02-06  1:44                                                   ` YOSHIFUJI Hideaki / 吉藤英明
  2007-02-06 12:51                                                 ` Neil Horman
  1 sibling, 1 reply; 71+ messages in thread
From: David Miller @ 2007-02-06  1:32 UTC (permalink / raw)
  To: yoshfuji
  Cc: nhorman, vladislav.yasevich, sri, kuznet, pekkas, jmorris, kaber, netdev

From: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date: Tue, 06 Feb 2007 10:24:05 +0900 (JST)

> > @@ -498,7 +500,8 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
> >          msg->icmph.icmp6_unused = 0;
> >          msg->icmph.icmp6_router    = router;
> >          msg->icmph.icmp6_solicited = solicited;
> > -        msg->icmph.icmp6_override  = override;
> > +	msg->icmph.icmp6_override  = override;
> > +
> >  
> >          /* Set the target address. */
> >  	ipv6_addr_copy(&msg->target, solicited_addr);
> 
> Why do you "change" this?

He edited it, but then reverted his change but in the
process added proper tab characters :-)  It is easy
to miss things like this.

This whole file has a lot of lines lacking proper tab
characters.

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-06  1:44                                                   ` YOSHIFUJI Hideaki / 吉藤英明
@ 2007-02-06  1:43                                                     ` David Miller
  0 siblings, 0 replies; 71+ messages in thread
From: David Miller @ 2007-02-06  1:43 UTC (permalink / raw)
  To: yoshfuji
  Cc: nhorman, vladislav.yasevich, sri, kuznet, pekkas, jmorris, kaber, netdev

From: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date: Tue, 06 Feb 2007 10:44:08 +0900 (JST)

> Yes, I agree, but I think it is different issue, and
> maybe, we should check and change other places as well,
> in separate patch(es).

I agree.

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-06  1:32                                                 ` David Miller
@ 2007-02-06  1:44                                                   ` YOSHIFUJI Hideaki / 吉藤英明
  2007-02-06  1:43                                                     ` David Miller
  0 siblings, 1 reply; 71+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2007-02-06  1:44 UTC (permalink / raw)
  To: davem
  Cc: nhorman, vladislav.yasevich, sri, kuznet, pekkas, jmorris, kaber,
	netdev, yoshfuji

In article <20070205.173258.102576664.davem@davemloft.net> (at Mon, 05 Feb 2007 17:32:58 -0800 (PST)), David Miller <davem@davemloft.net> says:

> From: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
> Date: Tue, 06 Feb 2007 10:24:05 +0900 (JST)
> 
> > > @@ -498,7 +500,8 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
> > >          msg->icmph.icmp6_unused = 0;
> > >          msg->icmph.icmp6_router    = router;
> > >          msg->icmph.icmp6_solicited = solicited;
> > > -        msg->icmph.icmp6_override  = override;
> > > +	msg->icmph.icmp6_override  = override;
> > > +
> > >  
> > >          /* Set the target address. */
> > >  	ipv6_addr_copy(&msg->target, solicited_addr);
> > 
> > Why do you "change" this?
> 
> He edited it, but then reverted his change but in the
> process added proper tab characters :-)  It is easy
> to miss things like this.

I know, you know. :-)

> This whole file has a lot of lines lacking proper tab
> characters.

Yes, I agree, but I think it is different issue, and
maybe, we should check and change other places as well,
in separate patch(es).

--yoshfuji

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-06  1:24                                               ` YOSHIFUJI Hideaki / 吉藤英明
  2007-02-06  1:32                                                 ` David Miller
@ 2007-02-06 12:51                                                 ` Neil Horman
  2007-02-06 20:09                                                   ` Neil Horman
  1 sibling, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-02-06 12:51 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber, netdev

On Tue, Feb 06, 2007 at 10:24:05AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
> Hello.
> 
<snip>
Thank you Yoshifuji.  To be honest, I've been focused on functionality rather
than pretty so far, figuring I'd clean up the tabbing from my various patch
inclusions/reversions when we had agreed that the functionality looked good.
I'll fix up my tabbing/spacing, as well as your other comments in my next post.

Regards
Neil


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-06 12:51                                                 ` Neil Horman
@ 2007-02-06 20:09                                                   ` Neil Horman
  2007-02-06 21:13                                                     ` Vlad Yasevich
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-02-06 20:09 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber, netdev

On Tue, Feb 06, 2007 at 07:51:44AM -0500, Neil Horman wrote:
> On Tue, Feb 06, 2007 at 10:24:05AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
> > Hello.
> > 
> <snip>
> Thank you Yoshifuji.  To be honest, I've been focused on functionality rather

Ok, New patch attached, taking Vlad and Yoshifujis latest comments into account.
Vlad, as you mentioned, I too looked and saw that we already join the all nodes
multicast address in ipv6_add_dev, so the additional anycast join is incorrect,
and has been removed.  Whitespace/tabs have been cleaned up, code has been
consolidated/beautified as per suggestions.  I'm testing now, and early results
are good.

Thanks & Regards
Neil

Signed-off-by: Neil Horman <nhorman@tuxdriver.com>


 include/linux/if_addr.h |    1 
 include/linux/ipv6.h    |    4 ++
 include/linux/sysctl.h  |    1 
 include/net/addrconf.h  |    4 +-
 net/ipv6/Kconfig        |   10 +++++
 net/ipv6/addrconf.c     |   93 ++++++++++++++++++++++++++++++++++++++++--------
 net/ipv6/ip6_output.c   |   28 ++++++++++++++
 net/ipv6/mcast.c        |    4 +-
 net/ipv6/ndisc.c        |   84 ++++++++++++++++++++++++++++++-------------
 9 files changed, 187 insertions(+), 42 deletions(-)


diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
index d557e4c..43f3bed 100644
--- a/include/linux/if_addr.h
+++ b/include/linux/if_addr.h
@@ -39,6 +39,7 @@ enum
 #define IFA_F_TEMPORARY		IFA_F_SECONDARY
 
 #define	IFA_F_NODAD		0x02
+#define IFA_F_OPTIMISTIC	0x04
 #define	IFA_F_HOMEADDRESS	0x10
 #define IFA_F_DEPRECATED	0x20
 #define IFA_F_TENTATIVE		0x40
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index f824113..bf93c1b 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -177,6 +177,9 @@ struct ipv6_devconf {
 #endif
 #endif
 	__s32		proxy_ndp;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	__s32		optimistic_dad;
+#endif
 	void		*sysctl;
 };
 
@@ -205,6 +208,7 @@ enum {
 	DEVCONF_RTR_PROBE_INTERVAL,
 	DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN,
 	DEVCONF_PROXY_NDP,
+	DEVCONF_OPTIMISTIC_DAD,
 	DEVCONF_MAX
 };
 
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 81480e6..972a33a 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -570,6 +570,7 @@ enum {
 	NET_IPV6_RTR_PROBE_INTERVAL=21,
 	NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22,
 	NET_IPV6_PROXY_NDP=23,
+	NET_IPV6_OPTIMISTIC_DAD=24,
 	__NET_IPV6_MAX
 };
 
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 88df8fc..d248a19 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -73,7 +73,9 @@ extern int			ipv6_get_saddr(struct dst_entry *dst,
 extern int			ipv6_dev_get_saddr(struct net_device *dev, 
 					       struct in6_addr *daddr,
 					       struct in6_addr *saddr);
-extern int			ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
+extern int			ipv6_get_lladdr(struct net_device *dev, 
+						struct in6_addr *,
+						unsigned char banned_flags);
 extern int			ipv6_rcv_saddr_equal(const struct sock *sk, 
 						      const struct sock *sk2);
 extern void			addrconf_join_solict(struct net_device *dev,
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index deb4101..822d3eb 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -57,6 +57,16 @@ config IPV6_ROUTE_INFO
 
 	  If unsure, say N.
 
+config IPV6_OPTIMISTIC_DAD
+	bool "IPv6: Enable RFC 4429 Optimistic DAD (EXPERIMENTAL)"
+	depends on IPV6 && EXPERIMENTAL
+	---help---
+	  This is experimental support for optimistic Duplicate
+	  Address Detection.  It allows for autoconfigured addresses
+	  to be used more quickly.
+
+	  If unsure, say N.
+
 config INET6_AH
 	tristate "IPv6: AH transformation"
 	depends on IPV6
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index e385469..f2bf7da 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -594,6 +594,16 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
 
 	ifa->rt = rt;
 
+	/*
+	 * 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 out nexhop router
+	 */
+
+	if (rt->rt6i_nexthop == NULL)
+		ifa->flags &= ~IFA_F_OPTIMISTIC;
+
 	ifa->idev = idev;
 	in6_dev_hold(idev);
 	/* For caller */
@@ -770,6 +780,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
 	int tmp_plen;
 	int ret = 0;
 	int max_addresses;
+	u32 addr_flags;
 
 	write_lock(&idev->lock);
 	if (ift) {
@@ -827,10 +838,17 @@ retry:
 	spin_unlock_bh(&ifp->lock);
 
 	write_unlock(&idev->lock);
+
+	addr_flags = IFA_F_TEMPORARY;
+	/* set in addrconf_prefix_rcv() */
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addr_flags |= IFA_F_OPTIMISTIC;
+
 	ift = !max_addresses ||
 	      ipv6_count_addresses(idev) < max_addresses ? 
 		ipv6_add_addr(idev, &addr, tmp_plen,
-			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
+			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
+			      addr_flags) : NULL;
 	if (!ift || IS_ERR(ift)) {
 		in6_ifa_put(ifp);
 		in6_dev_put(idev);
@@ -962,13 +980,14 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
 			 * - Tentative Address (RFC2462 section 5.4)
 			 *  - A tentative address is not considered
 			 *    "assigned to an interface" in the traditional
-			 *    sense.
+			 *    sense, unless it is also flagged as optimistic.
 			 * - Candidate Source Address (section 4)
 			 *  - In any case, anycast addresses, multicast
 			 *    addresses, and the unspecified address MUST
 			 *    NOT be included in a candidate set.
 			 */
-			if (ifa->flags & IFA_F_TENTATIVE)
+			if ((ifa->flags & IFA_F_TENTATIVE) &&
+			    (!(ifa->flags & IFA_F_OPTIMISTIC)))
 				continue;
 			if (unlikely(score.addr_type == IPV6_ADDR_ANY ||
 				     score.addr_type & IPV6_ADDR_MULTICAST)) {
@@ -1027,15 +1046,17 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
 				}
 			}
 
-			/* Rule 3: Avoid deprecated address */
+			/* Rule 3: Avoid deprecated and optimistic address */
 			if (hiscore.rule < 3) {
 				if (ipv6_saddr_preferred(hiscore.addr_type) ||
-				    !(ifa_result->flags & IFA_F_DEPRECATED))
+				   (((ifa_result->flags &
+				    (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0)))
 					hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				hiscore.rule++;
 			}
 			if (ipv6_saddr_preferred(score.addr_type) ||
-			    !(ifa->flags & IFA_F_DEPRECATED)) {
+			   (((ifa_result->flags &
+			    (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0))) {
 				score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
 					score.rule = 3;
@@ -1174,7 +1195,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
 }
 
 
-int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
+int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, 
+		    unsigned char banned_flags)
 {
 	struct inet6_dev *idev;
 	int err = -EADDRNOTAVAIL;
@@ -1185,7 +1207,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
 
 		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 && !(ifp->flags & banned_flags)) {
 				ipv6_addr_copy(addr, &ifp->addr);
 				err = 0;
 				break;
@@ -1735,6 +1757,13 @@ ok:
 
 		if (ifp == NULL && 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 &&
+			    !ipv6_devconf.forwarding)
+				addr_flags = IFA_F_OPTIMISTIC;
+#endif
 
 			/* Do not allow to create too much of autoconfigured
 			 * addresses; this would be too easy way to crash kernel.
@@ -1742,7 +1771,8 @@ ok:
 			if (!max_addresses ||
 			    ipv6_count_addresses(in6_dev) < max_addresses)
 				ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len,
-						    addr_type&IPV6_ADDR_SCOPE_MASK, 0);
+						    addr_type&IPV6_ADDR_SCOPE_MASK,
+						    addr_flags);
 
 			if (!ifp || IS_ERR(ifp)) {
 				in6_dev_put(in6_dev);
@@ -1945,7 +1975,11 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
 		ifp->prefered_lft = prefered_lft;
 		ifp->tstamp = jiffies;
 		spin_unlock_bh(&ifp->lock);
-
+		/*
+		 * Note that section 3.1 of RFC 4429 indicates
+		 * That the Optimistic flag should not be set for
+		 * manually configured addresses
+		 */
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
 		addrconf_verify(0);
@@ -2122,8 +2156,16 @@ static void init_loopback(struct net_device *dev)
 static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr)
 {
 	struct inet6_ifaddr * ifp;
+	u32 addr_flags = IFA_F_PERMANENT;
+
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	if (idev->cnf.optimistic_dad &&
+	    !ipv6_devconf.forwarding)
+		addr_flags |= IFA_F_OPTIMISTIC;
+#endif
+
 
-	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
+	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, addr_flags);
 	if (!IS_ERR(ifp)) {
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
@@ -2190,7 +2232,7 @@ ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
 {
 	struct in6_addr lladdr;
 
-	if (!ipv6_get_lladdr(link_dev, &lladdr)) {
+	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
 		addrconf_add_linklocal(idev, &lladdr);
 		return 0;
 	}
@@ -2527,7 +2569,11 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp)
 	unsigned long rand_num;
 	struct inet6_dev *idev = ifp->idev;
 
-	rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		rand_num = 0;
+	else
+		rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+
 	ifp->probes = idev->cnf.dad_transmits;
 	addrconf_mod_timer(ifp, AC_DAD, rand_num);
 }
@@ -2553,7 +2599,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
 	    !(ifp->flags&IFA_F_TENTATIVE) ||
 	    ifp->flags & IFA_F_NODAD) {
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -2597,7 +2643,7 @@ static void addrconf_dad_timer(unsigned long data)
 		 * DAD was successful
 		 */
 
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -2627,6 +2673,9 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
 	 *	Configure the address for reception. Now it is valid.
 	 */
 
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addrconf_leave_anycast(ifp);
+
 	ipv6_ifa_notify(RTM_NEWADDR, ifp);
 
 	/* If added prefix is link local and forwarding is off,
@@ -3398,6 +3447,9 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
 #endif
 #endif
 	array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad;
+#endif
 }
 
 static inline size_t inet6_if_nlmsg_size(void)
@@ -3917,6 +3969,17 @@ static struct addrconf_sysctl_table
 			.mode		=	0644,
 			.proc_handler	=	&proc_dointvec,
 		},
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+		{
+			.ctl_name	=	NET_IPV6_OPTIMISTIC_DAD,
+			.procname       =       "optimistic_dad",
+			.data           =       &ipv6_devconf.optimistic_dad,
+			.maxlen         =       sizeof(int),
+			.mode           =       0644,
+			.proc_handler   =       &proc_dointvec,
+
+		},
+#endif
 		{
 			.ctl_name	=	0,	/* sentinel */
 		}
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 7b7bd44..b8a5261 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -857,6 +857,34 @@ static int ip6_dst_lookup_tail(struct sock *sk,
 
 	if (ipv6_addr_any(&fl->fl6_src)) {
 		err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src);
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+		/*
+		 * Here if the dst entry we've looked up 
+		 * has a neighbour entry that is in the INCOMPLETE
+		 * state and the src address from the flow is 
+		 * marked as OPTIMISTIC, we release the found 
+		 * dst entry and replace it instead with the 
+		 * dst entry of the nexthop router
+		 */
+		if (!((*dst)->neighbour->nud_state & NUD_VALID)) {
+			struct inet6_ifaddr *ifp;
+			struct flowi fl_gw;
+			ifp = ipv6_get_ifaddr(&fl->fl6_src, (*dst)->dev, 1);
+
+			if (ifp && ifp->flags & IFA_F_OPTIMISTIC) {
+				/*
+				 * We need to get the dst entry for the 
+				 * default router instead
+				 */
+				dst_release(*dst);
+				memcpy(&fl_gw, fl, sizeof(struct flowi));
+				memset(&fl_gw.fl6_dst, 0, sizeof(struct in6_addr));
+				*dst = ip6_route_output(sk, &fl_gw);
+				if ((err = (*dst)->error))
+					goto out_err_release;				
+			}
+		}
+#endif
 		if (err)
 			goto out_err_release;
 	}
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 882cde4..9c5273c 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
@@ -1789,7 +1789,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 39bb658..7400805 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -447,6 +447,8 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
 	ifp = ipv6_get_ifaddr(solicited_addr, dev, 1);
  	if (ifp) {
 		src_addr = solicited_addr;
+		if (ifp->flags & IFA_F_OPTIMISTIC)
+			override = 0;
 		in6_ifa_put(ifp);
 	} else {
 		if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr))
@@ -500,6 +502,7 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
         msg->icmph.icmp6_solicited = solicited;
         msg->icmph.icmp6_override  = override;
 
+
         /* Set the target address. */
 	ipv6_addr_copy(&msg->target, solicited_addr);
 
@@ -542,7 +545,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
 	int send_llinfo;
 
 	if (saddr == NULL) {
-		if (ipv6_get_lladdr(dev, &addr_buf))
+		if (ipv6_get_lladdr(dev, &addr_buf, 
+				   (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
 			return;
 		saddr = &addr_buf;
 	}
@@ -559,7 +563,7 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
 		return;
 
 	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
-	send_llinfo = dev->addr_len && !ipv6_addr_any(saddr);
+	send_llinfo = dev->addr_len && !ipv6_addr_any(saddr); 
 	if (send_llinfo)
 		len += ndisc_opt_addr_space(dev);
 
@@ -622,9 +626,29 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
         struct sk_buff *skb;
         struct icmp6hdr *hdr;
 	__u8 * opt;
+	struct inet6_ifaddr *ifp;
+	int send_sllao = 1;
         int len;
 	int err;
 
+	/*
+	 * Check the source address.  If its OPTIMISTIC
+	 * and addr_len is non-zero (implying the sllao option)
+	 * then don't send the RS (RFC 4429, section 2.2)
+	 */
+	ifp = ipv6_get_ifaddr(saddr, dev, 1);
+
+	/*
+	 * According to section 2.2 of RFC 4429, we must not 
+	 * send router solicitations with a sllao from 
+	 * optimistic addresses, but we may send the solicitation
+	 * if we don't include the sallo.  So here we check
+	 * if our address is optimistic, and if so, we
+	 * supress the inclusion of the sllao.
+	 */ 
+	if (!dev->addr_len || !ifp || (ifp->flags & IFA_F_OPTIMISTIC))
+		send_sllao=0;
+
 	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
 			dev->ifindex);
 
@@ -637,7 +661,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
 		return;
 
 	len = sizeof(struct icmp6hdr);
-	if (dev->addr_len)
+	if (dev->addr_len && send_sllao)
 		len += ndisc_opt_addr_space(dev);
 
         skb = sock_alloc_send_skb(sk,
@@ -664,7 +688,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
 
 	opt = (u8*) (hdr + 1);
 
-	if (dev->addr_len)
+	if (dev->addr_len && send_sllao)
 		ndisc_fill_addr_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr,
 				       dev->addr_len, dev->type);
 
@@ -746,6 +770,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	int dad = ipv6_addr_any(saddr);
 	int inc;
 	int is_router;
+	int type;
 
 	if (ipv6_addr_is_multicast(&msg->target)) {
 		ND_PRINTK2(KERN_WARNING 
@@ -796,28 +821,39 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	inc = ipv6_addr_is_multicast(daddr);
 
 	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
-		if (ifp->flags & IFA_F_TENTATIVE) {
-			/* Address is tentative. If the source
-			   is unspecified address, it is someone
-			   does DAD, otherwise we ignore solicitations
-			   until DAD timer expires.
-			 */
-			if (!dad)
+
+		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
+			if (dad) {
+				if (dev->type == ARPHRD_IEEE802_TR) {
+					unsigned char *sadr = skb->mac.raw;
+					if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
+					    sadr[9] == dev->dev_addr[1] &&
+					    sadr[10] == dev->dev_addr[2] &&
+					    sadr[11] == dev->dev_addr[3] &&
+					    sadr[12] == dev->dev_addr[4] &&
+					    sadr[13] == dev->dev_addr[5]) {
+						/* looped-back to us */
+						goto out;
+					}
+				}
+
+				/*
+				 * We are colliding with another node
+				 * who is doing DAD
+				 * so fail our DAD process
+				 */
+				addrconf_dad_failure(ifp);
 				goto out;
-			if (dev->type == ARPHRD_IEEE802_TR) {
-				unsigned char *sadr = skb->mac.raw;
-				if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
-				    sadr[9] == dev->dev_addr[1] &&
-				    sadr[10] == dev->dev_addr[2] &&
-				    sadr[11] == dev->dev_addr[3] &&
-				    sadr[12] == dev->dev_addr[4] &&
-				    sadr[13] == dev->dev_addr[5]) {
-					/* looped-back to us */
+			} else {
+				/*
+				 * This is not a dad solicitation.
+				 * If we are an optimistic node, 
+				 * we should respond.  
+				 * Otherwise, we should ignore it. 
+				 */
+				if (!(ifp->flags & IFA_F_OPTIMISTIC))
 					goto out;
-				}
 			}
-			addrconf_dad_failure(ifp); 
-			return;
 		}
 
 		idev = ifp->idev;
@@ -1406,7 +1442,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
 
 	dev = skb->dev;
 
-	if (ipv6_get_lladdr(dev, &saddr_buf)) {
+	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
 		ND_PRINTK2(KERN_WARNING
 			   "ICMPv6 Redirect: no link-local address on %s\n",
 			   dev->name);

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-06 20:09                                                   ` Neil Horman
@ 2007-02-06 21:13                                                     ` Vlad Yasevich
  2007-02-07 20:55                                                       ` Neil Horman
  0 siblings, 1 reply; 71+ messages in thread
From: Vlad Yasevich @ 2007-02-06 21:13 UTC (permalink / raw)
  To: Neil Horman
  Cc: YOSHIFUJI Hideaki / 吉藤英明,
	sri, davem, kuznet, pekkas, jmorris, kaber, netdev

Hi Neil

a few comments.  sorry, just can't resist... :)


> @@ -2627,6 +2673,9 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
>  	 *	Configure the address for reception. Now it is valid.
>  	 */
>  
> +	if (ifp->flags & IFA_F_OPTIMISTIC)
> +		addrconf_leave_anycast(ifp);
> +
>  	ipv6_ifa_notify(RTM_NEWADDR, ifp);

Since we are no longer doing anycast, remove the above.

> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
> index 7b7bd44..b8a5261 100644
> --- a/net/ipv6/ip6_output.c
> +++ b/net/ipv6/ip6_output.c
> @@ -857,6 +857,34 @@ static int ip6_dst_lookup_tail(struct sock *sk,
>  
>  	if (ipv6_addr_any(&fl->fl6_src)) {
>  		err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src);
> +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> +		/*
> +		 * Here if the dst entry we've looked up 
> +		 * has a neighbour entry that is in the INCOMPLETE
> +		 * state and the src address from the flow is 
> +		 * marked as OPTIMISTIC, we release the found 
> +		 * dst entry and replace it instead with the 
> +		 * dst entry of the nexthop router
> +		 */
> +		if (!((*dst)->neighbour->nud_state & NUD_VALID)) {
> +			struct inet6_ifaddr *ifp;
> +			struct flowi fl_gw;
> +			ifp = ipv6_get_ifaddr(&fl->fl6_src, (*dst)->dev, 1);
> +
> +			if (ifp && ifp->flags & IFA_F_OPTIMISTIC) {
> +				/*
> +				 * We need to get the dst entry for the 
> +				 * default router instead
> +				 */
> +				dst_release(*dst);
> +				memcpy(&fl_gw, fl, sizeof(struct flowi));
> +				memset(&fl_gw.fl6_dst, 0, sizeof(struct in6_addr));
> +				*dst = ip6_route_output(sk, &fl_gw);
> +				if ((err = (*dst)->error))
> +					goto out_err_release;				
> +			}
> +		}
> +#endif
>  		if (err)
>  			goto out_err_release;

This actually doesn't look right....  What if ipv6_get_saddr returned an error?
I think the new code should go after the 'goto out_err_release;'...
That way, we do optimistic checks if we have a valid source address.

>  	}
> diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
> index 882cde4..9c5273c 100644
> --- a/net/ipv6/mcast.c
> +++ b/net/ipv6/mcast.c
> @@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
>  
>  	skb_reserve(skb, LL_RESERVED_SPACE(dev));
>  
> -	if (ipv6_get_lladdr(dev, &addr_buf)) {
> +	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
>  		/* <draft-ietf-magma-mld-source-05.txt>:
>  		 * use unspecified address as the source address 
>  		 * when a valid link-local address is not available.
> @@ -1789,7 +1789,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
>  
>  	skb_reserve(skb, LL_RESERVED_SPACE(dev));
>  
> -	if (ipv6_get_lladdr(dev, &addr_buf)) {
> +	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
>  		/* <draft-ietf-magma-mld-source-05.txt>:
>  		 * use unspecified address as the source address 
>  		 * when a valid link-local address is not available.
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index 39bb658..7400805 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -447,6 +447,8 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
>  	ifp = ipv6_get_ifaddr(solicited_addr, dev, 1);
>   	if (ifp) {
>  		src_addr = solicited_addr;
> +		if (ifp->flags & IFA_F_OPTIMISTIC)
> +			override = 0;
>  		in6_ifa_put(ifp);
>  	} else {
>  		if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr))
> @@ -500,6 +502,7 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
>          msg->icmph.icmp6_solicited = solicited;
>          msg->icmph.icmp6_override  = override;
>  
> +

Empty line...

>          /* Set the target address. */
>  	ipv6_addr_copy(&msg->target, solicited_addr);
>  
> @@ -542,7 +545,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
>  	int send_llinfo;
>  
>  	if (saddr == NULL) {
> -		if (ipv6_get_lladdr(dev, &addr_buf))
> +		if (ipv6_get_lladdr(dev, &addr_buf, 
> +				   (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
>  			return;
>  		saddr = &addr_buf;
>  	}
> @@ -559,7 +563,7 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
>  		return;
>  
>  	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
> -	send_llinfo = dev->addr_len && !ipv6_addr_any(saddr);
> +	send_llinfo = dev->addr_len && !ipv6_addr_any(saddr); 

space to tab conversion???

>  	if (send_llinfo)
>  		len += ndisc_opt_addr_space(dev);
>  
> @@ -622,9 +626,29 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
>          struct sk_buff *skb;
>          struct icmp6hdr *hdr;
>  	__u8 * opt;
> +	struct inet6_ifaddr *ifp;
> +	int send_sllao = 1;
>          int len;
>  	int err;
>  
> +	/*
> +	 * Check the source address.  If its OPTIMISTIC
> +	 * and addr_len is non-zero (implying the sllao option)
> +	 * then don't send the RS (RFC 4429, section 2.2)
> +	 */
> +	ifp = ipv6_get_ifaddr(saddr, dev, 1);
> +
> +	/*
> +	 * According to section 2.2 of RFC 4429, we must not 
> +	 * send router solicitations with a sllao from 
> +	 * optimistic addresses, but we may send the solicitation
> +	 * if we don't include the sallo.  So here we check
> +	 * if our address is optimistic, and if so, we
> +	 * supress the inclusion of the sllao.
> +	 */ 
> +	if (!dev->addr_len || !ifp || (ifp->flags & IFA_F_OPTIMISTIC))
> +		send_sllao=0;
> +
>  	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
>  			dev->ifindex);
>  
> @@ -637,7 +661,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
>  		return;
>  
>  	len = sizeof(struct icmp6hdr);
> -	if (dev->addr_len)
> +	if (dev->addr_len && send_sllao)
>  		len += ndisc_opt_addr_space(dev);
>  
>          skb = sock_alloc_send_skb(sk,
> @@ -664,7 +688,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
>  
>  	opt = (u8*) (hdr + 1);
>  
> -	if (dev->addr_len)
> +	if (dev->addr_len && send_sllao)
>  		ndisc_fill_addr_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr,
>  				       dev->addr_len, dev->type);
>  
> @@ -746,6 +770,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
>  	int dad = ipv6_addr_any(saddr);
>  	int inc;
>  	int is_router;
> +	int type;

'type' is unused.

>  
>  	if (ipv6_addr_is_multicast(&msg->target)) {
>  		ND_PRINTK2(KERN_WARNING 
> @@ -796,28 +821,39 @@ static void ndisc_recv_ns(struct sk_buff *skb)
>  	inc = ipv6_addr_is_multicast(daddr);
>  
>  	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
> -		if (ifp->flags & IFA_F_TENTATIVE) {
> -			/* Address is tentative. If the source
> -			   is unspecified address, it is someone
> -			   does DAD, otherwise we ignore solicitations
> -			   until DAD timer expires.
> -			 */
> -			if (!dad)
> +
> +		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
> +			if (dad) {
> +				if (dev->type == ARPHRD_IEEE802_TR) {
> +					unsigned char *sadr = skb->mac.raw;
> +					if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
> +					    sadr[9] == dev->dev_addr[1] &&
> +					    sadr[10] == dev->dev_addr[2] &&
> +					    sadr[11] == dev->dev_addr[3] &&
> +					    sadr[12] == dev->dev_addr[4] &&
> +					    sadr[13] == dev->dev_addr[5]) {
> +						/* looped-back to us */
> +						goto out;
> +					}
> +				}
> +
> +				/*
> +				 * We are colliding with another node
> +				 * who is doing DAD
> +				 * so fail our DAD process
> +				 */
> +				addrconf_dad_failure(ifp);
>  				goto out;
> -			if (dev->type == ARPHRD_IEEE802_TR) {
> -				unsigned char *sadr = skb->mac.raw;
> -				if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
> -				    sadr[9] == dev->dev_addr[1] &&
> -				    sadr[10] == dev->dev_addr[2] &&
> -				    sadr[11] == dev->dev_addr[3] &&
> -				    sadr[12] == dev->dev_addr[4] &&
> -				    sadr[13] == dev->dev_addr[5]) {
> -					/* looped-back to us */
> +			} else {
> +				/*
> +				 * This is not a dad solicitation.
> +				 * If we are an optimistic node, 
> +				 * we should respond.  
> +				 * Otherwise, we should ignore it. 
> +				 */
> +				if (!(ifp->flags & IFA_F_OPTIMISTIC))
>  					goto out;
> -				}
>  			}
> -			addrconf_dad_failure(ifp); 
> -			return;
>  		}
>  
>  		idev = ifp->idev;
> @@ -1406,7 +1442,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
>  
>  	dev = skb->dev;
>  
> -	if (ipv6_get_lladdr(dev, &saddr_buf)) {
> +	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
>  		ND_PRINTK2(KERN_WARNING
>  			   "ICMPv6 Redirect: no link-local address on %s\n",
>  			   dev->name);
> 

Regards
-vlad

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-06 21:13                                                     ` Vlad Yasevich
@ 2007-02-07 20:55                                                       ` Neil Horman
  2007-02-07 21:19                                                         ` Vlad Yasevich
                                                                           ` (2 more replies)
  0 siblings, 3 replies; 71+ messages in thread
From: Neil Horman @ 2007-02-07 20:55 UTC (permalink / raw)
  To: Vlad Yasevich
  Cc: YOSHIFUJI Hideaki / 吉藤英明,
	sri, davem, kuznet, pekkas, jmorris, kaber, netdev

On Tue, Feb 06, 2007 at 04:13:25PM -0500, Vlad Yasevich wrote:
> Hi Neil
> 
> a few comments.  sorry, just can't resist... :)
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

Its really ok :).  Brian found some other minor typos as well.  I've attached
the latest patch which takes your comments, and Brians observations into
account.  The only change I left out was the space/tab conversion in
ndisc_send_ns, since it appeared to be more inline with the style in the rest of
the file.  Everything else is incorporated into this patch, however. Thanks for
all the keen eyes!

Thanks & Regards
Neil

Signed-off-by: Neil Horman <nhorman@tuxdriver.com>


 include/linux/if_addr.h |    1 
 include/linux/ipv6.h    |    4 ++
 include/linux/sysctl.h  |    1 
 include/net/addrconf.h  |    4 +-
 net/ipv6/Kconfig        |   10 +++++
 net/ipv6/addrconf.c     |   90 ++++++++++++++++++++++++++++++++++++++++--------
 net/ipv6/ip6_output.c   |   28 ++++++++++++++
 net/ipv6/mcast.c        |    4 +-
 net/ipv6/ndisc.c        |   82 ++++++++++++++++++++++++++++++-------------
 9 files changed, 182 insertions(+), 42 deletions(-)



diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
index d557e4c..43f3bed 100644
--- a/include/linux/if_addr.h
+++ b/include/linux/if_addr.h
@@ -39,6 +39,7 @@ enum
 #define IFA_F_TEMPORARY		IFA_F_SECONDARY
 
 #define	IFA_F_NODAD		0x02
+#define IFA_F_OPTIMISTIC	0x04
 #define	IFA_F_HOMEADDRESS	0x10
 #define IFA_F_DEPRECATED	0x20
 #define IFA_F_TENTATIVE		0x40
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index f824113..bf93c1b 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -177,6 +177,9 @@ struct ipv6_devconf {
 #endif
 #endif
 	__s32		proxy_ndp;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	__s32		optimistic_dad;
+#endif
 	void		*sysctl;
 };
 
@@ -205,6 +208,7 @@ enum {
 	DEVCONF_RTR_PROBE_INTERVAL,
 	DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN,
 	DEVCONF_PROXY_NDP,
+	DEVCONF_OPTIMISTIC_DAD,
 	DEVCONF_MAX
 };
 
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 81480e6..972a33a 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -570,6 +570,7 @@ enum {
 	NET_IPV6_RTR_PROBE_INTERVAL=21,
 	NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22,
 	NET_IPV6_PROXY_NDP=23,
+	NET_IPV6_OPTIMISTIC_DAD=24,
 	__NET_IPV6_MAX
 };
 
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 88df8fc..d248a19 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -73,7 +73,9 @@ extern int			ipv6_get_saddr(struct dst_entry *dst,
 extern int			ipv6_dev_get_saddr(struct net_device *dev, 
 					       struct in6_addr *daddr,
 					       struct in6_addr *saddr);
-extern int			ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
+extern int			ipv6_get_lladdr(struct net_device *dev, 
+						struct in6_addr *,
+						unsigned char banned_flags);
 extern int			ipv6_rcv_saddr_equal(const struct sock *sk, 
 						      const struct sock *sk2);
 extern void			addrconf_join_solict(struct net_device *dev,
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index deb4101..822d3eb 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -57,6 +57,16 @@ config IPV6_ROUTE_INFO
 
 	  If unsure, say N.
 
+config IPV6_OPTIMISTIC_DAD
+	bool "IPv6: Enable RFC 4429 Optimistic DAD (EXPERIMENTAL)"
+	depends on IPV6 && EXPERIMENTAL
+	---help---
+	  This is experimental support for optimistic Duplicate
+	  Address Detection.  It allows for autoconfigured addresses
+	  to be used more quickly.
+
+	  If unsure, say N.
+
 config INET6_AH
 	tristate "IPv6: AH transformation"
 	depends on IPV6
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index e385469..c884eeb 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -594,6 +594,16 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
 
 	ifa->rt = rt;
 
+	/*
+	 * 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 (rt->rt6i_nexthop == NULL)
+		ifa->flags &= ~IFA_F_OPTIMISTIC;
+
 	ifa->idev = idev;
 	in6_dev_hold(idev);
 	/* For caller */
@@ -770,6 +780,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
 	int tmp_plen;
 	int ret = 0;
 	int max_addresses;
+	u32 addr_flags;
 
 	write_lock(&idev->lock);
 	if (ift) {
@@ -827,10 +838,17 @@ retry:
 	spin_unlock_bh(&ifp->lock);
 
 	write_unlock(&idev->lock);
+
+	addr_flags = IFA_F_TEMPORARY;
+	/* set in addrconf_prefix_rcv() */
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addr_flags |= IFA_F_OPTIMISTIC;
+
 	ift = !max_addresses ||
 	      ipv6_count_addresses(idev) < max_addresses ? 
 		ipv6_add_addr(idev, &addr, tmp_plen,
-			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
+			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
+			      addr_flags) : NULL;
 	if (!ift || IS_ERR(ift)) {
 		in6_ifa_put(ifp);
 		in6_dev_put(idev);
@@ -962,13 +980,14 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
 			 * - Tentative Address (RFC2462 section 5.4)
 			 *  - A tentative address is not considered
 			 *    "assigned to an interface" in the traditional
-			 *    sense.
+			 *    sense, unless it is also flagged as optimistic.
 			 * - Candidate Source Address (section 4)
 			 *  - In any case, anycast addresses, multicast
 			 *    addresses, and the unspecified address MUST
 			 *    NOT be included in a candidate set.
 			 */
-			if (ifa->flags & IFA_F_TENTATIVE)
+			if ((ifa->flags & IFA_F_TENTATIVE) &&
+			    (!(ifa->flags & IFA_F_OPTIMISTIC)))
 				continue;
 			if (unlikely(score.addr_type == IPV6_ADDR_ANY ||
 				     score.addr_type & IPV6_ADDR_MULTICAST)) {
@@ -1027,15 +1046,17 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
 				}
 			}
 
-			/* Rule 3: Avoid deprecated address */
+			/* Rule 3: Avoid deprecated and optimistic addresses */
 			if (hiscore.rule < 3) {
 				if (ipv6_saddr_preferred(hiscore.addr_type) ||
-				    !(ifa_result->flags & IFA_F_DEPRECATED))
+				   (((ifa_result->flags &
+				    (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0)))
 					hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				hiscore.rule++;
 			}
 			if (ipv6_saddr_preferred(score.addr_type) ||
-			    !(ifa->flags & IFA_F_DEPRECATED)) {
+			   (((ifa_result->flags &
+			    (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0))) {
 				score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
 					score.rule = 3;
@@ -1174,7 +1195,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
 }
 
 
-int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
+int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, 
+		    unsigned char banned_flags)
 {
 	struct inet6_dev *idev;
 	int err = -EADDRNOTAVAIL;
@@ -1185,7 +1207,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
 
 		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 && !(ifp->flags & banned_flags)) {
 				ipv6_addr_copy(addr, &ifp->addr);
 				err = 0;
 				break;
@@ -1735,6 +1757,13 @@ ok:
 
 		if (ifp == NULL && 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 &&
+			    !ipv6_devconf.forwarding)
+				addr_flags = IFA_F_OPTIMISTIC;
+#endif
 
 			/* Do not allow to create too much of autoconfigured
 			 * addresses; this would be too easy way to crash kernel.
@@ -1742,7 +1771,8 @@ ok:
 			if (!max_addresses ||
 			    ipv6_count_addresses(in6_dev) < max_addresses)
 				ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len,
-						    addr_type&IPV6_ADDR_SCOPE_MASK, 0);
+						    addr_type&IPV6_ADDR_SCOPE_MASK,
+						    addr_flags);
 
 			if (!ifp || IS_ERR(ifp)) {
 				in6_dev_put(in6_dev);
@@ -1945,7 +1975,11 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
 		ifp->prefered_lft = prefered_lft;
 		ifp->tstamp = jiffies;
 		spin_unlock_bh(&ifp->lock);
-
+		/*
+		 * Note that section 3.1 of RFC 4429 indicates
+		 * that the Optimistic flag should not be set for
+		 * manually configured addresses
+		 */
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
 		addrconf_verify(0);
@@ -2122,8 +2156,16 @@ static void init_loopback(struct net_device *dev)
 static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr)
 {
 	struct inet6_ifaddr * ifp;
+	u32 addr_flags = IFA_F_PERMANENT;
 
-	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	if (idev->cnf.optimistic_dad &&
+	    !ipv6_devconf.forwarding)
+		addr_flags |= IFA_F_OPTIMISTIC;
+#endif
+
+
+	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, addr_flags);
 	if (!IS_ERR(ifp)) {
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
@@ -2190,7 +2232,7 @@ ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
 {
 	struct in6_addr lladdr;
 
-	if (!ipv6_get_lladdr(link_dev, &lladdr)) {
+	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
 		addrconf_add_linklocal(idev, &lladdr);
 		return 0;
 	}
@@ -2527,7 +2569,11 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp)
 	unsigned long rand_num;
 	struct inet6_dev *idev = ifp->idev;
 
-	rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		rand_num = 0;
+	else
+		rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+
 	ifp->probes = idev->cnf.dad_transmits;
 	addrconf_mod_timer(ifp, AC_DAD, rand_num);
 }
@@ -2553,7 +2599,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
 	    !(ifp->flags&IFA_F_TENTATIVE) ||
 	    ifp->flags & IFA_F_NODAD) {
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -2597,7 +2643,7 @@ static void addrconf_dad_timer(unsigned long data)
 		 * DAD was successful
 		 */
 
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -3398,6 +3444,9 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
 #endif
 #endif
 	array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad;
+#endif
 }
 
 static inline size_t inet6_if_nlmsg_size(void)
@@ -3917,6 +3966,17 @@ static struct addrconf_sysctl_table
 			.mode		=	0644,
 			.proc_handler	=	&proc_dointvec,
 		},
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+		{
+			.ctl_name	=	NET_IPV6_OPTIMISTIC_DAD,
+			.procname       =       "optimistic_dad",
+			.data           =       &ipv6_devconf.optimistic_dad,
+			.maxlen         =       sizeof(int),
+			.mode           =       0644,
+			.proc_handler   =       &proc_dointvec,
+
+		},
+#endif
 		{
 			.ctl_name	=	0,	/* sentinel */
 		}
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 7b7bd44..8a1ea96 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -859,6 +859,34 @@ static int ip6_dst_lookup_tail(struct sock *sk,
 		err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src);
 		if (err)
 			goto out_err_release;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+		/*
+		 * Here if the dst entry we've looked up 
+		 * has a neighbour entry that is in the INCOMPLETE
+		 * state and the src address from the flow is 
+		 * marked as OPTIMISTIC, we release the found 
+		 * dst entry and replace it instead with the 
+		 * dst entry of the nexthop router
+		 */
+		if (!((*dst)->neighbour->nud_state & NUD_VALID)) {
+			struct inet6_ifaddr *ifp;
+			struct flowi fl_gw;
+			ifp = ipv6_get_ifaddr(&fl->fl6_src, (*dst)->dev, 1);
+
+			if (ifp && ifp->flags & IFA_F_OPTIMISTIC) {
+				/*
+				 * We need to get the dst entry for the 
+				 * default router instead
+				 */
+				dst_release(*dst);
+				memcpy(&fl_gw, fl, sizeof(struct flowi));
+				memset(&fl_gw.fl6_dst, 0, sizeof(struct in6_addr));
+				*dst = ip6_route_output(sk, &fl_gw);
+				if ((err = (*dst)->error))
+					goto out_err_release;				
+			}
+		}
+#endif
 	}
 
 	return 0;
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 882cde4..9c5273c 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
@@ -1789,7 +1789,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 39bb658..919db04 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -447,6 +447,8 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
 	ifp = ipv6_get_ifaddr(solicited_addr, dev, 1);
  	if (ifp) {
 		src_addr = solicited_addr;
+		if (ifp->flags & IFA_F_OPTIMISTIC)
+			override = 0;
 		in6_ifa_put(ifp);
 	} else {
 		if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr))
@@ -542,7 +544,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
 	int send_llinfo;
 
 	if (saddr == NULL) {
-		if (ipv6_get_lladdr(dev, &addr_buf))
+		if (ipv6_get_lladdr(dev, &addr_buf, 
+				   (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
 			return;
 		saddr = &addr_buf;
 	}
@@ -559,7 +562,7 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
 		return;
 
 	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
-	send_llinfo = dev->addr_len && !ipv6_addr_any(saddr);
+	send_llinfo = dev->addr_len && !ipv6_addr_any(saddr); 
 	if (send_llinfo)
 		len += ndisc_opt_addr_space(dev);
 
@@ -622,9 +625,29 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
         struct sk_buff *skb;
         struct icmp6hdr *hdr;
 	__u8 * opt;
+	struct inet6_ifaddr *ifp;
+	int send_sllao = 1;
         int len;
 	int err;
 
+	/*
+	 * Check the source address.  If its OPTIMISTIC
+	 * and addr_len is non-zero (implying the sllao option)
+	 * then don't send the RS (RFC 4429, section 2.2)
+	 */
+	ifp = ipv6_get_ifaddr(saddr, dev, 1);
+
+	/*
+	 * According to section 2.2 of RFC 4429, we must not 
+	 * send router solicitations with a sllao from 
+	 * optimistic addresses, but we may send the solicitation
+	 * if we don't include the sllao.  So here we check
+	 * if our address is optimistic, and if so, we
+	 * supress the inclusion of the sllao.
+	 */ 
+	if (!dev->addr_len || !ifp || (ifp->flags & IFA_F_OPTIMISTIC))
+		send_sllao=0;
+
 	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
 			dev->ifindex);
 
@@ -637,7 +660,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
 		return;
 
 	len = sizeof(struct icmp6hdr);
-	if (dev->addr_len)
+	if (dev->addr_len && send_sllao)
 		len += ndisc_opt_addr_space(dev);
 
         skb = sock_alloc_send_skb(sk,
@@ -664,7 +687,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
 
 	opt = (u8*) (hdr + 1);
 
-	if (dev->addr_len)
+	if (dev->addr_len && send_sllao)
 		ndisc_fill_addr_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr,
 				       dev->addr_len, dev->type);
 
@@ -796,28 +819,39 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	inc = ipv6_addr_is_multicast(daddr);
 
 	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
-		if (ifp->flags & IFA_F_TENTATIVE) {
-			/* Address is tentative. If the source
-			   is unspecified address, it is someone
-			   does DAD, otherwise we ignore solicitations
-			   until DAD timer expires.
-			 */
-			if (!dad)
+
+		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
+			if (dad) {
+				if (dev->type == ARPHRD_IEEE802_TR) {
+					unsigned char *sadr = skb->mac.raw;
+					if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
+					    sadr[9] == dev->dev_addr[1] &&
+					    sadr[10] == dev->dev_addr[2] &&
+					    sadr[11] == dev->dev_addr[3] &&
+					    sadr[12] == dev->dev_addr[4] &&
+					    sadr[13] == dev->dev_addr[5]) {
+						/* looped-back to us */
+						goto out;
+					}
+				}
+
+				/*
+				 * We are colliding with another node
+				 * who is doing DAD
+				 * so fail our DAD process
+				 */
+				addrconf_dad_failure(ifp);
 				goto out;
-			if (dev->type == ARPHRD_IEEE802_TR) {
-				unsigned char *sadr = skb->mac.raw;
-				if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
-				    sadr[9] == dev->dev_addr[1] &&
-				    sadr[10] == dev->dev_addr[2] &&
-				    sadr[11] == dev->dev_addr[3] &&
-				    sadr[12] == dev->dev_addr[4] &&
-				    sadr[13] == dev->dev_addr[5]) {
-					/* looped-back to us */
+			} else {
+				/*
+				 * This is not a dad solicitation.
+				 * If we are an optimistic node, 
+				 * we should respond.  
+				 * Otherwise, we should ignore it. 
+				 */
+				if (!(ifp->flags & IFA_F_OPTIMISTIC))
 					goto out;
-				}
 			}
-			addrconf_dad_failure(ifp); 
-			return;
 		}
 
 		idev = ifp->idev;
@@ -1406,7 +1440,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
 
 	dev = skb->dev;
 
-	if (ipv6_get_lladdr(dev, &saddr_buf)) {
+	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
 		ND_PRINTK2(KERN_WARNING
 			   "ICMPv6 Redirect: no link-local address on %s\n",
 			   dev->name);

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-07 20:55                                                       ` Neil Horman
@ 2007-02-07 21:19                                                         ` Vlad Yasevich
  2007-02-07 21:52                                                         ` YOSHIFUJI Hideaki / 吉藤英明
  2007-02-07 22:26                                                         ` YOSHIFUJI Hideaki / 吉藤英明
  2 siblings, 0 replies; 71+ messages in thread
From: Vlad Yasevich @ 2007-02-07 21:19 UTC (permalink / raw)
  To: Neil Horman
  Cc: YOSHIFUJI Hideaki / 吉藤英明,
	davem, kuznet, pekkas, jmorris, kaber, netdev

Darn... and it was looking so good...


> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
> index 7b7bd44..8a1ea96 100644
> --- a/net/ipv6/ip6_output.c
> +++ b/net/ipv6/ip6_output.c
> @@ -859,6 +859,34 @@ static int ip6_dst_lookup_tail(struct sock *sk,
>  		err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src);
>  		if (err)
>  			goto out_err_release;
> +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> +		/*
> +		 * Here if the dst entry we've looked up 
> +		 * has a neighbour entry that is in the INCOMPLETE
> +		 * state and the src address from the flow is 
> +		 * marked as OPTIMISTIC, we release the found 
> +		 * dst entry and replace it instead with the 
> +		 * dst entry of the nexthop router
> +		 */
> +		if (!((*dst)->neighbour->nud_state & NUD_VALID)) {
> +			struct inet6_ifaddr *ifp;
> +			struct flowi fl_gw;
> +			ifp = ipv6_get_ifaddr(&fl->fl6_src, (*dst)->dev, 1);
> +
> +			if (ifp && ifp->flags & IFA_F_OPTIMISTIC) {
> +				/*
> +				 * We need to get the dst entry for the 
> +				 * default router instead
> +				 */
> +				dst_release(*dst);
> +				memcpy(&fl_gw, fl, sizeof(struct flowi));
> +				memset(&fl_gw.fl6_dst, 0, sizeof(struct in6_addr));
> +				*dst = ip6_route_output(sk, &fl_gw);
> +				if ((err = (*dst)->error))
> +					goto out_err_release;				
> +			}
> +		}
> +#endif

You are leaking an 'ifp' ref.  ipv6_get_ifaddr() does in6_ifa_hold(ifp).  You
need do in6_ifa_put(ifp), when you are done with the ifp.



> @@ -622,9 +625,29 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
>          struct sk_buff *skb;
>          struct icmp6hdr *hdr;
>  	__u8 * opt;
> +	struct inet6_ifaddr *ifp;
> +	int send_sllao = 1;
>          int len;
>  	int err;
>  
> +	/*
> +	 * Check the source address.  If its OPTIMISTIC
> +	 * and addr_len is non-zero (implying the sllao option)
> +	 * then don't send the RS (RFC 4429, section 2.2)
> +	 */
> +	ifp = ipv6_get_ifaddr(saddr, dev, 1);
> +
> +	/*
> +	 * According to section 2.2 of RFC 4429, we must not 
> +	 * send router solicitations with a sllao from 
> +	 * optimistic addresses, but we may send the solicitation
> +	 * if we don't include the sllao.  So here we check
> +	 * if our address is optimistic, and if so, we
> +	 * supress the inclusion of the sllao.
> +	 */ 
> +	if (!dev->addr_len || !ifp || (ifp->flags & IFA_F_OPTIMISTIC))
> +		send_sllao=0;
> +

Ditto.


-vlad

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-07 20:55                                                       ` Neil Horman
  2007-02-07 21:19                                                         ` Vlad Yasevich
@ 2007-02-07 21:52                                                         ` YOSHIFUJI Hideaki / 吉藤英明
  2007-02-08 13:07                                                           ` Neil Horman
  2007-02-07 22:26                                                         ` YOSHIFUJI Hideaki / 吉藤英明
  2 siblings, 1 reply; 71+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2007-02-07 21:52 UTC (permalink / raw)
  To: nhorman
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber,
	netdev, yoshfuji

In article <20070207205503.GB20804@hmsreliant.homelinux.net> (at Wed, 7 Feb 2007 15:55:03 -0500), Neil Horman <nhorman@tuxdriver.com> says:

> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
> index 7b7bd44..8a1ea96 100644
> --- a/net/ipv6/ip6_output.c
> +++ b/net/ipv6/ip6_output.c
> @@ -859,6 +859,34 @@ static int ip6_dst_lookup_tail(struct sock *sk,
>  		err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src);
>  		if (err)
>  			goto out_err_release;
> +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> +		/*
> +		 * Here if the dst entry we've looked up 
> +		 * has a neighbour entry that is in the INCOMPLETE
> +		 * state and the src address from the flow is 
> +		 * marked as OPTIMISTIC, we release the found 
> +		 * dst entry and replace it instead with the 
> +		 * dst entry of the nexthop router
> +		 */
> +		if (!((*dst)->neighbour->nud_state & NUD_VALID)) {
> +			struct inet6_ifaddr *ifp;
> +			struct flowi fl_gw;
> +			ifp = ipv6_get_ifaddr(&fl->fl6_src, (*dst)->dev, 1);
> +
> +			if (ifp && ifp->flags & IFA_F_OPTIMISTIC) {
> +				/*
> +				 * We need to get the dst entry for the 
> +				 * default router instead
> +				 */
> +				dst_release(*dst);
> +				memcpy(&fl_gw, fl, sizeof(struct flowi));
> +				memset(&fl_gw.fl6_dst, 0, sizeof(struct in6_addr));
> +				*dst = ip6_route_output(sk, &fl_gw);
> +				if ((err = (*dst)->error))
> +					goto out_err_release;				
> +			}
> +		}
> +#endif
>  	}
>  
>  	return 0;

Sorry, this is still not correct if the source address is already
specified.  I think they should be placed just betwee laste "}" and
"return 0;", no?


I still have a question.  Now, who will install the kernel route for
the incoming packet?  Can we get packet for our unicast address during
optimistic DAD period?

--yoshfuji

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-07 20:55                                                       ` Neil Horman
  2007-02-07 21:19                                                         ` Vlad Yasevich
  2007-02-07 21:52                                                         ` YOSHIFUJI Hideaki / 吉藤英明
@ 2007-02-07 22:26                                                         ` YOSHIFUJI Hideaki / 吉藤英明
  2007-02-08 16:41                                                           ` Neil Horman
  2 siblings, 1 reply; 71+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2007-02-07 22:26 UTC (permalink / raw)
  To: nhorman
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber,
	netdev, yoshfuji

In article <20070207205503.GB20804@hmsreliant.homelinux.net> (at Wed, 7 Feb 2007 15:55:03 -0500), Neil Horman <nhorman@tuxdriver.com> says:

> @@ -559,7 +562,7 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
>  		return;
>  
>  	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
> -	send_llinfo = dev->addr_len && !ipv6_addr_any(saddr);
> +	send_llinfo = dev->addr_len && !ipv6_addr_any(saddr); 
>  	if (send_llinfo)
>  		len += ndisc_opt_addr_space(dev);
>  

trailing space

> @@ -637,7 +660,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
>  		return;
>  
>  	len = sizeof(struct icmp6hdr);
> -	if (dev->addr_len)
> +	if (dev->addr_len && send_sllao)
>  		len += ndisc_opt_addr_space(dev);
>  
>          skb = sock_alloc_send_skb(sk,

if (send_sllao)

> @@ -664,7 +687,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
>  
>  	opt = (u8*) (hdr + 1);
>  
> -	if (dev->addr_len)
> +	if (dev->addr_len && send_sllao)
>  		ndisc_fill_addr_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr,
>  				       dev->addr_len, dev->type);
>  

ditto.

--yoshfuji

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-07 21:52                                                         ` YOSHIFUJI Hideaki / 吉藤英明
@ 2007-02-08 13:07                                                           ` Neil Horman
  2007-02-12 23:27                                                             ` YOSHIFUJI Hideaki / 吉藤英明
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-02-08 13:07 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber, netdev

On Thu, Feb 08, 2007 at 06:52:06AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
> In article <20070207205503.GB20804@hmsreliant.homelinux.net> (at Wed, 7 Feb 2007 15:55:03 -0500), Neil Horman <nhorman@tuxdriver.com> says:
> 
> > diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
> > index 7b7bd44..8a1ea96 100644
> > --- a/net/ipv6/ip6_output.c
> > +++ b/net/ipv6/ip6_output.c
> > @@ -859,6 +859,34 @@ static int ip6_dst_lookup_tail(struct sock *sk,
> >  		err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src);
> >  		if (err)
> >  			goto out_err_release;
> > +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> > +		/*
> > +		 * Here if the dst entry we've looked up 
> > +		 * has a neighbour entry that is in the INCOMPLETE
> > +		 * state and the src address from the flow is 
> > +		 * marked as OPTIMISTIC, we release the found 
> > +		 * dst entry and replace it instead with the 
> > +		 * dst entry of the nexthop router
> > +		 */
> > +		if (!((*dst)->neighbour->nud_state & NUD_VALID)) {
> > +			struct inet6_ifaddr *ifp;
> > +			struct flowi fl_gw;
> > +			ifp = ipv6_get_ifaddr(&fl->fl6_src, (*dst)->dev, 1);
> > +
> > +			if (ifp && ifp->flags & IFA_F_OPTIMISTIC) {
> > +				/*
> > +				 * We need to get the dst entry for the 
> > +				 * default router instead
> > +				 */
> > +				dst_release(*dst);
> > +				memcpy(&fl_gw, fl, sizeof(struct flowi));
> > +				memset(&fl_gw.fl6_dst, 0, sizeof(struct in6_addr));
> > +				*dst = ip6_route_output(sk, &fl_gw);
> > +				if ((err = (*dst)->error))
> > +					goto out_err_release;				
> > +			}
> > +		}
> > +#endif
> >  	}
> >  
> >  	return 0;
> 
> Sorry, this is still not correct if the source address is already
> specified.  I think they should be placed just betwee laste "}" and
> "return 0;", no?
> 
I think moving it would be no more correct or incorrect, but it would be less
efficient.  ipv6_addr_any will only return true if the source address is the
anycast address (::).  That address will never be optimistic.  So we could
certainly move  the optimistic code to where you indicate, but then we'd be
checking the code for optimistic address without any need?  Or is there
something I'm missing?

> 
> I still have a question.  Now, who will install the kernel route for
> the incoming packet?  Can we get packet for our unicast address during
> optimistic DAD period?
> 
Not sure what you're getting at here.  RFC 4429 makes no distinction between
optimistic and non-optimistic packets for recevied frames, so routes for
received packets will be added by the same code that does it currently (which I
admittedly haven't looked into to closely).  Packets received for our unicast
address (even if it is optimistic) are handled just as any other packet is (same
as it is for TENTATIVE addresses, as I understand it). 

Regards
Neil

> --yoshfuji
> -
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-07 22:26                                                         ` YOSHIFUJI Hideaki / 吉藤英明
@ 2007-02-08 16:41                                                           ` Neil Horman
  2007-02-08 17:10                                                             ` YOSHIFUJI Hideaki / 吉藤英明
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-02-08 16:41 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber, netdev

On Thu, Feb 08, 2007 at 07:26:20AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
> In article <20070207205503.GB20804@hmsreliant.homelinux.net> (at Wed, 7 Feb 2007 15:55:03 -0500), Neil Horman <nhorman@tuxdriver.com> says:
> 
> > @@ -559,7 +562,7 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
> >  		return;
> >  
> >  	len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
> > -	send_llinfo = dev->addr_len && !ipv6_addr_any(saddr);
> > +	send_llinfo = dev->addr_len && !ipv6_addr_any(saddr); 
> >  	if (send_llinfo)
> >  		len += ndisc_opt_addr_space(dev);
> >  
> 
> trailing space
> 
> > @@ -637,7 +660,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
> >  		return;
> >  
> >  	len = sizeof(struct icmp6hdr);
> > -	if (dev->addr_len)
> > +	if (dev->addr_len && send_sllao)
> >  		len += ndisc_opt_addr_space(dev);
> >  
> >          skb = sock_alloc_send_skb(sk,
> 
> if (send_sllao)
> 
> > @@ -664,7 +687,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
> >  
> >  	opt = (u8*) (hdr + 1);
> >  
> > -	if (dev->addr_len)
> > +	if (dev->addr_len && send_sllao)
> >  		ndisc_fill_addr_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr,
> >  				       dev->addr_len, dev->type);
> >  
> 
> ditto.
> 
> --yoshfuji


Ok, new patch attached.  Fixes up Yoshfuji's requested changes above, as well
as Vlads observed ifaddr reference leak.  Let me know what you think.

Thanks & Regards
Neil

Signed-off-by: Neil Horman <nhorman@tuxdriver.com>


 include/linux/if_addr.h |    1 
 include/linux/ipv6.h    |    4 ++
 include/linux/sysctl.h  |    1 
 include/net/addrconf.h  |    4 +-
 net/ipv6/Kconfig        |   10 +++++
 net/ipv6/addrconf.c     |   90 ++++++++++++++++++++++++++++++++++++++++--------
 net/ipv6/ip6_output.c   |   34 ++++++++++++++++++
 net/ipv6/mcast.c        |    4 +-
 net/ipv6/ndisc.c        |   80 ++++++++++++++++++++++++++++++------------
 9 files changed, 187 insertions(+), 41 deletions(-)



diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
index d557e4c..43f3bed 100644
--- a/include/linux/if_addr.h
+++ b/include/linux/if_addr.h
@@ -39,6 +39,7 @@ enum
 #define IFA_F_TEMPORARY		IFA_F_SECONDARY
 
 #define	IFA_F_NODAD		0x02
+#define IFA_F_OPTIMISTIC	0x04
 #define	IFA_F_HOMEADDRESS	0x10
 #define IFA_F_DEPRECATED	0x20
 #define IFA_F_TENTATIVE		0x40
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index f824113..bf93c1b 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -177,6 +177,9 @@ struct ipv6_devconf {
 #endif
 #endif
 	__s32		proxy_ndp;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	__s32		optimistic_dad;
+#endif
 	void		*sysctl;
 };
 
@@ -205,6 +208,7 @@ enum {
 	DEVCONF_RTR_PROBE_INTERVAL,
 	DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN,
 	DEVCONF_PROXY_NDP,
+	DEVCONF_OPTIMISTIC_DAD,
 	DEVCONF_MAX
 };
 
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 81480e6..972a33a 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -570,6 +570,7 @@ enum {
 	NET_IPV6_RTR_PROBE_INTERVAL=21,
 	NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22,
 	NET_IPV6_PROXY_NDP=23,
+	NET_IPV6_OPTIMISTIC_DAD=24,
 	__NET_IPV6_MAX
 };
 
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 88df8fc..d248a19 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -73,7 +73,9 @@ extern int			ipv6_get_saddr(struct dst_entry *dst,
 extern int			ipv6_dev_get_saddr(struct net_device *dev, 
 					       struct in6_addr *daddr,
 					       struct in6_addr *saddr);
-extern int			ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
+extern int			ipv6_get_lladdr(struct net_device *dev, 
+						struct in6_addr *,
+						unsigned char banned_flags);
 extern int			ipv6_rcv_saddr_equal(const struct sock *sk, 
 						      const struct sock *sk2);
 extern void			addrconf_join_solict(struct net_device *dev,
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index deb4101..822d3eb 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -57,6 +57,16 @@ config IPV6_ROUTE_INFO
 
 	  If unsure, say N.
 
+config IPV6_OPTIMISTIC_DAD
+	bool "IPv6: Enable RFC 4429 Optimistic DAD (EXPERIMENTAL)"
+	depends on IPV6 && EXPERIMENTAL
+	---help---
+	  This is experimental support for optimistic Duplicate
+	  Address Detection.  It allows for autoconfigured addresses
+	  to be used more quickly.
+
+	  If unsure, say N.
+
 config INET6_AH
 	tristate "IPv6: AH transformation"
 	depends on IPV6
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index e385469..c884eeb 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -594,6 +594,16 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
 
 	ifa->rt = rt;
 
+	/*
+	 * 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 (rt->rt6i_nexthop == NULL)
+		ifa->flags &= ~IFA_F_OPTIMISTIC;
+
 	ifa->idev = idev;
 	in6_dev_hold(idev);
 	/* For caller */
@@ -770,6 +780,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
 	int tmp_plen;
 	int ret = 0;
 	int max_addresses;
+	u32 addr_flags;
 
 	write_lock(&idev->lock);
 	if (ift) {
@@ -827,10 +838,17 @@ retry:
 	spin_unlock_bh(&ifp->lock);
 
 	write_unlock(&idev->lock);
+
+	addr_flags = IFA_F_TEMPORARY;
+	/* set in addrconf_prefix_rcv() */
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addr_flags |= IFA_F_OPTIMISTIC;
+
 	ift = !max_addresses ||
 	      ipv6_count_addresses(idev) < max_addresses ? 
 		ipv6_add_addr(idev, &addr, tmp_plen,
-			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
+			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
+			      addr_flags) : NULL;
 	if (!ift || IS_ERR(ift)) {
 		in6_ifa_put(ifp);
 		in6_dev_put(idev);
@@ -962,13 +980,14 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
 			 * - Tentative Address (RFC2462 section 5.4)
 			 *  - A tentative address is not considered
 			 *    "assigned to an interface" in the traditional
-			 *    sense.
+			 *    sense, unless it is also flagged as optimistic.
 			 * - Candidate Source Address (section 4)
 			 *  - In any case, anycast addresses, multicast
 			 *    addresses, and the unspecified address MUST
 			 *    NOT be included in a candidate set.
 			 */
-			if (ifa->flags & IFA_F_TENTATIVE)
+			if ((ifa->flags & IFA_F_TENTATIVE) &&
+			    (!(ifa->flags & IFA_F_OPTIMISTIC)))
 				continue;
 			if (unlikely(score.addr_type == IPV6_ADDR_ANY ||
 				     score.addr_type & IPV6_ADDR_MULTICAST)) {
@@ -1027,15 +1046,17 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
 				}
 			}
 
-			/* Rule 3: Avoid deprecated address */
+			/* Rule 3: Avoid deprecated and optimistic addresses */
 			if (hiscore.rule < 3) {
 				if (ipv6_saddr_preferred(hiscore.addr_type) ||
-				    !(ifa_result->flags & IFA_F_DEPRECATED))
+				   (((ifa_result->flags &
+				    (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0)))
 					hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				hiscore.rule++;
 			}
 			if (ipv6_saddr_preferred(score.addr_type) ||
-			    !(ifa->flags & IFA_F_DEPRECATED)) {
+			   (((ifa_result->flags &
+			    (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0))) {
 				score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
 					score.rule = 3;
@@ -1174,7 +1195,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
 }
 
 
-int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
+int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, 
+		    unsigned char banned_flags)
 {
 	struct inet6_dev *idev;
 	int err = -EADDRNOTAVAIL;
@@ -1185,7 +1207,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
 
 		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 && !(ifp->flags & banned_flags)) {
 				ipv6_addr_copy(addr, &ifp->addr);
 				err = 0;
 				break;
@@ -1735,6 +1757,13 @@ ok:
 
 		if (ifp == NULL && 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 &&
+			    !ipv6_devconf.forwarding)
+				addr_flags = IFA_F_OPTIMISTIC;
+#endif
 
 			/* Do not allow to create too much of autoconfigured
 			 * addresses; this would be too easy way to crash kernel.
@@ -1742,7 +1771,8 @@ ok:
 			if (!max_addresses ||
 			    ipv6_count_addresses(in6_dev) < max_addresses)
 				ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len,
-						    addr_type&IPV6_ADDR_SCOPE_MASK, 0);
+						    addr_type&IPV6_ADDR_SCOPE_MASK,
+						    addr_flags);
 
 			if (!ifp || IS_ERR(ifp)) {
 				in6_dev_put(in6_dev);
@@ -1945,7 +1975,11 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
 		ifp->prefered_lft = prefered_lft;
 		ifp->tstamp = jiffies;
 		spin_unlock_bh(&ifp->lock);
-
+		/*
+		 * Note that section 3.1 of RFC 4429 indicates
+		 * that the Optimistic flag should not be set for
+		 * manually configured addresses
+		 */
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
 		addrconf_verify(0);
@@ -2122,8 +2156,16 @@ static void init_loopback(struct net_device *dev)
 static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr)
 {
 	struct inet6_ifaddr * ifp;
+	u32 addr_flags = IFA_F_PERMANENT;
 
-	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	if (idev->cnf.optimistic_dad &&
+	    !ipv6_devconf.forwarding)
+		addr_flags |= IFA_F_OPTIMISTIC;
+#endif
+
+
+	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, addr_flags);
 	if (!IS_ERR(ifp)) {
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
@@ -2190,7 +2232,7 @@ ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
 {
 	struct in6_addr lladdr;
 
-	if (!ipv6_get_lladdr(link_dev, &lladdr)) {
+	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
 		addrconf_add_linklocal(idev, &lladdr);
 		return 0;
 	}
@@ -2527,7 +2569,11 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp)
 	unsigned long rand_num;
 	struct inet6_dev *idev = ifp->idev;
 
-	rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		rand_num = 0;
+	else
+		rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+
 	ifp->probes = idev->cnf.dad_transmits;
 	addrconf_mod_timer(ifp, AC_DAD, rand_num);
 }
@@ -2553,7 +2599,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
 	    !(ifp->flags&IFA_F_TENTATIVE) ||
 	    ifp->flags & IFA_F_NODAD) {
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -2597,7 +2643,7 @@ static void addrconf_dad_timer(unsigned long data)
 		 * DAD was successful
 		 */
 
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -3398,6 +3444,9 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
 #endif
 #endif
 	array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad;
+#endif
 }
 
 static inline size_t inet6_if_nlmsg_size(void)
@@ -3917,6 +3966,17 @@ static struct addrconf_sysctl_table
 			.mode		=	0644,
 			.proc_handler	=	&proc_dointvec,
 		},
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+		{
+			.ctl_name	=	NET_IPV6_OPTIMISTIC_DAD,
+			.procname       =       "optimistic_dad",
+			.data           =       &ipv6_devconf.optimistic_dad,
+			.maxlen         =       sizeof(int),
+			.mode           =       0644,
+			.proc_handler   =       &proc_dointvec,
+
+		},
+#endif
 		{
 			.ctl_name	=	0,	/* sentinel */
 		}
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 7b7bd44..07a5f4d 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -859,6 +859,40 @@ static int ip6_dst_lookup_tail(struct sock *sk,
 		err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src);
 		if (err)
 			goto out_err_release;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+		/*
+		 * Here if the dst entry we've looked up 
+		 * has a neighbour entry that is in the INCOMPLETE
+		 * state and the src address from the flow is 
+		 * marked as OPTIMISTIC, we release the found 
+		 * dst entry and replace it instead with the 
+		 * dst entry of the nexthop router
+		 */
+		if (!((*dst)->neighbour->nud_state & NUD_VALID)) {
+			struct inet6_ifaddr *ifp;
+			struct flowi fl_gw;
+			int redirect;
+
+			ifp = ipv6_get_ifaddr(&fl->fl6_src, (*dst)->dev, 1);
+
+			redirect = (ifp && ifp->flags & IFA_F_OPTIMISTIC);
+			if (ifp)
+				in6_ifa_put(ifp);
+
+			if (redirect) {
+				/*
+				 * We need to get the dst entry for the 
+				 * default router instead
+				 */
+				dst_release(*dst);
+				memcpy(&fl_gw, fl, sizeof(struct flowi));
+				memset(&fl_gw.fl6_dst, 0, sizeof(struct in6_addr));
+				*dst = ip6_route_output(sk, &fl_gw);
+				if ((err = (*dst)->error))
+					goto out_err_release;				
+			}
+		}
+#endif
 	}
 
 	return 0;
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 882cde4..9c5273c 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
@@ -1789,7 +1789,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 39bb658..9656018 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -447,6 +447,8 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
 	ifp = ipv6_get_ifaddr(solicited_addr, dev, 1);
  	if (ifp) {
 		src_addr = solicited_addr;
+		if (ifp->flags & IFA_F_OPTIMISTIC)
+			override = 0;
 		in6_ifa_put(ifp);
 	} else {
 		if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr))
@@ -542,7 +544,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
 	int send_llinfo;
 
 	if (saddr == NULL) {
-		if (ipv6_get_lladdr(dev, &addr_buf))
+		if (ipv6_get_lladdr(dev, &addr_buf, 
+				   (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
 			return;
 		saddr = &addr_buf;
 	}
@@ -622,9 +625,29 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
         struct sk_buff *skb;
         struct icmp6hdr *hdr;
 	__u8 * opt;
+	struct inet6_ifaddr *ifp;
+	int send_sllao = 1;
         int len;
 	int err;
 
+	/*
+	 * Check the source address.  If its OPTIMISTIC
+	 * and addr_len is non-zero (implying the sllao option)
+	 * then don't send the RS (RFC 4429, section 2.2)
+	 */
+	ifp = ipv6_get_ifaddr(saddr, dev, 1);
+
+	/*
+	 * According to section 2.2 of RFC 4429, we must not 
+	 * send router solicitations with a sllao from 
+	 * optimistic addresses, but we may send the solicitation
+	 * if we don't include the sllao.  So here we check
+	 * if our address is optimistic, and if so, we
+	 * supress the inclusion of the sllao.
+	 */ 
+	if (!dev->addr_len || !ifp || (ifp->flags & IFA_F_OPTIMISTIC))
+		send_sllao=0;
+
 	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
 			dev->ifindex);
 
@@ -637,7 +660,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
 		return;
 
 	len = sizeof(struct icmp6hdr);
-	if (dev->addr_len)
+	if (send_sllao)
 		len += ndisc_opt_addr_space(dev);
 
         skb = sock_alloc_send_skb(sk,
@@ -664,7 +687,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
 
 	opt = (u8*) (hdr + 1);
 
-	if (dev->addr_len)
+	if (send_sllao)
 		ndisc_fill_addr_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr,
 				       dev->addr_len, dev->type);
 
@@ -796,28 +819,39 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	inc = ipv6_addr_is_multicast(daddr);
 
 	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
-		if (ifp->flags & IFA_F_TENTATIVE) {
-			/* Address is tentative. If the source
-			   is unspecified address, it is someone
-			   does DAD, otherwise we ignore solicitations
-			   until DAD timer expires.
-			 */
-			if (!dad)
+
+		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
+			if (dad) {
+				if (dev->type == ARPHRD_IEEE802_TR) {
+					unsigned char *sadr = skb->mac.raw;
+					if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
+					    sadr[9] == dev->dev_addr[1] &&
+					    sadr[10] == dev->dev_addr[2] &&
+					    sadr[11] == dev->dev_addr[3] &&
+					    sadr[12] == dev->dev_addr[4] &&
+					    sadr[13] == dev->dev_addr[5]) {
+						/* looped-back to us */
+						goto out;
+					}
+				}
+
+				/*
+				 * We are colliding with another node
+				 * who is doing DAD
+				 * so fail our DAD process
+				 */
+				addrconf_dad_failure(ifp);
 				goto out;
-			if (dev->type == ARPHRD_IEEE802_TR) {
-				unsigned char *sadr = skb->mac.raw;
-				if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
-				    sadr[9] == dev->dev_addr[1] &&
-				    sadr[10] == dev->dev_addr[2] &&
-				    sadr[11] == dev->dev_addr[3] &&
-				    sadr[12] == dev->dev_addr[4] &&
-				    sadr[13] == dev->dev_addr[5]) {
-					/* looped-back to us */
+			} else {
+				/*
+				 * This is not a dad solicitation.
+				 * If we are an optimistic node, 
+				 * we should respond.  
+				 * Otherwise, we should ignore it. 
+				 */
+				if (!(ifp->flags & IFA_F_OPTIMISTIC))
 					goto out;
-				}
 			}
-			addrconf_dad_failure(ifp); 
-			return;
 		}
 
 		idev = ifp->idev;
@@ -1406,7 +1440,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
 
 	dev = skb->dev;
 
-	if (ipv6_get_lladdr(dev, &saddr_buf)) {
+	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
 		ND_PRINTK2(KERN_WARNING
 			   "ICMPv6 Redirect: no link-local address on %s\n",
 			   dev->name);

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-08 16:41                                                           ` Neil Horman
@ 2007-02-08 17:10                                                             ` YOSHIFUJI Hideaki / 吉藤英明
  2007-02-08 19:32                                                               ` Neil Horman
  0 siblings, 1 reply; 71+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2007-02-08 17:10 UTC (permalink / raw)
  To: nhorman
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber,
	netdev, yoshfuji

In article <20070208164151.GB27267@hmsreliant.homelinux.net> (at Thu, 8 Feb 2007 11:41:52 -0500), Neil Horman <nhorman@tuxdriver.com> says:

> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
> index 7b7bd44..07a5f4d 100644
> --- a/net/ipv6/ip6_output.c
> +++ b/net/ipv6/ip6_output.c
> @@ -859,6 +859,40 @@ static int ip6_dst_lookup_tail(struct sock *sk,
>  		err = ipv6_get_saddr(*dst, &fl->fl6_dst, &fl->fl6_src);
>  		if (err)
>  			goto out_err_release;
> +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> +		/*
> +		 * Here if the dst entry we've looked up 
> +		 * has a neighbour entry that is in the INCOMPLETE
> +		 * state and the src address from the flow is 
> +		 * marked as OPTIMISTIC, we release the found 
> +		 * dst entry and replace it instead with the 
> +		 * dst entry of the nexthop router
> +		 */
> +		if (!((*dst)->neighbour->nud_state & NUD_VALID)) {
> +			struct inet6_ifaddr *ifp;
> +			struct flowi fl_gw;
> +			int redirect;
> +
> +			ifp = ipv6_get_ifaddr(&fl->fl6_src, (*dst)->dev, 1);
> +
> +			redirect = (ifp && ifp->flags & IFA_F_OPTIMISTIC);
> +			if (ifp)
> +				in6_ifa_put(ifp);
> +
> +			if (redirect) {
> +				/*
> +				 * We need to get the dst entry for the 
> +				 * default router instead
> +				 */
> +				dst_release(*dst);
> +				memcpy(&fl_gw, fl, sizeof(struct flowi));
> +				memset(&fl_gw.fl6_dst, 0, sizeof(struct in6_addr));
> +				*dst = ip6_route_output(sk, &fl_gw);
> +				if ((err = (*dst)->error))
> +					goto out_err_release;				
> +			}
> +		}
> +#endif

These should be placed

>  	}

here. No?


> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index 39bb658..9656018 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -622,9 +625,29 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
>          struct sk_buff *skb;
>          struct icmp6hdr *hdr;
>  	__u8 * opt;
> +	struct inet6_ifaddr *ifp;
> +	int send_sllao = 1;
>          int len;
>  	int err;
>  
> +	/*
> +	 * Check the source address.  If its OPTIMISTIC
> +	 * and addr_len is non-zero (implying the sllao option)
> +	 * then don't send the RS (RFC 4429, section 2.2)
> +	 */
> +	ifp = ipv6_get_ifaddr(saddr, dev, 1);
> +
> +	/*
> +	 * According to section 2.2 of RFC 4429, we must not 
> +	 * send router solicitations with a sllao from 
> +	 * optimistic addresses, but we may send the solicitation
> +	 * if we don't include the sllao.  So here we check
> +	 * if our address is optimistic, and if so, we
> +	 * supress the inclusion of the sllao.
> +	 */ 
> +	if (!dev->addr_len || !ifp || (ifp->flags & IFA_F_OPTIMISTIC))
> +		send_sllao=0;
> +
>  	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
>  			dev->ifindex);
>  

Still leaking ifp.  Something like this?

int send_sllao = dev->addr_len;

#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
if (send_sllao) {
  struct inet6_ifaddr *ifp = ipv6_get_ifaddr(saddr, dev, 1);
  if (ifp) {
     if (ifp->flags & IFA_F_OPTIMISTIC)
       send_sllao = 0;
     in6_ifa_put(ifp);
  } else
     send_sllao = 0;
}
#endif

--yoshfuji

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-08 17:10                                                             ` YOSHIFUJI Hideaki / 吉藤英明
@ 2007-02-08 19:32                                                               ` Neil Horman
  2007-02-12 21:20                                                                 ` Neil Horman
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-02-08 19:32 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber, netdev

On Fri, Feb 09, 2007 at 02:10:37AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
> In article <20070208164151.GB27267@hmsreliant.homelinux.net> (at Thu, 8 Feb 2007 11:41:52 -0500), Neil Horman <nhorman@tuxdriver.com> says:
> 
><snip> 
> These should be placed
> 
> >  	}
> 
> here. No?
> 
Yes, you're right.  After re-reading the logic, its pretty evident that you want
to do the optimistic dad checks regardless of weather the flows initial source
address is the anycast address.

><snip>
> 
> Still leaking ifp.  Something like this?
> 
> int send_sllao = dev->addr_len;
> 
> #ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> if (send_sllao) {
>   struct inet6_ifaddr *ifp = ipv6_get_ifaddr(saddr, dev, 1);
>   if (ifp) {
>      if (ifp->flags & IFA_F_OPTIMISTIC)
>        send_sllao = 0;
>      in6_ifa_put(ifp);
>   } else
>      send_sllao = 0;
> }
> #endif
> 
Yeah, definately another leak.  You're fix looks good to me.

New patch attached with these fixes included


Thanks & Regards
Neil

Signed-off-by: Neil Horman <nhorman@tuxdriver.com>


 include/linux/if_addr.h |    1 
 include/linux/ipv6.h    |    4 ++
 include/linux/sysctl.h  |    1 
 include/net/addrconf.h  |    4 +-
 net/ipv6/Kconfig        |   10 +++++
 net/ipv6/addrconf.c     |   90 ++++++++++++++++++++++++++++++++++++++++--------
 net/ipv6/ip6_output.c   |   35 ++++++++++++++++++
 net/ipv6/mcast.c        |    4 +-
 net/ipv6/ndisc.c        |   84 ++++++++++++++++++++++++++++++++------------
 9 files changed, 192 insertions(+), 41 deletions(-)




diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
index d557e4c..43f3bed 100644
--- a/include/linux/if_addr.h
+++ b/include/linux/if_addr.h
@@ -39,6 +39,7 @@ enum
 #define IFA_F_TEMPORARY		IFA_F_SECONDARY
 
 #define	IFA_F_NODAD		0x02
+#define IFA_F_OPTIMISTIC	0x04
 #define	IFA_F_HOMEADDRESS	0x10
 #define IFA_F_DEPRECATED	0x20
 #define IFA_F_TENTATIVE		0x40
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index f824113..bf93c1b 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -177,6 +177,9 @@ struct ipv6_devconf {
 #endif
 #endif
 	__s32		proxy_ndp;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	__s32		optimistic_dad;
+#endif
 	void		*sysctl;
 };
 
@@ -205,6 +208,7 @@ enum {
 	DEVCONF_RTR_PROBE_INTERVAL,
 	DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN,
 	DEVCONF_PROXY_NDP,
+	DEVCONF_OPTIMISTIC_DAD,
 	DEVCONF_MAX
 };
 
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 81480e6..972a33a 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -570,6 +570,7 @@ enum {
 	NET_IPV6_RTR_PROBE_INTERVAL=21,
 	NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22,
 	NET_IPV6_PROXY_NDP=23,
+	NET_IPV6_OPTIMISTIC_DAD=24,
 	__NET_IPV6_MAX
 };
 
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 88df8fc..d248a19 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -73,7 +73,9 @@ extern int			ipv6_get_saddr(struct dst_entry *dst,
 extern int			ipv6_dev_get_saddr(struct net_device *dev, 
 					       struct in6_addr *daddr,
 					       struct in6_addr *saddr);
-extern int			ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
+extern int			ipv6_get_lladdr(struct net_device *dev, 
+						struct in6_addr *,
+						unsigned char banned_flags);
 extern int			ipv6_rcv_saddr_equal(const struct sock *sk, 
 						      const struct sock *sk2);
 extern void			addrconf_join_solict(struct net_device *dev,
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index deb4101..822d3eb 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -57,6 +57,16 @@ config IPV6_ROUTE_INFO
 
 	  If unsure, say N.
 
+config IPV6_OPTIMISTIC_DAD
+	bool "IPv6: Enable RFC 4429 Optimistic DAD (EXPERIMENTAL)"
+	depends on IPV6 && EXPERIMENTAL
+	---help---
+	  This is experimental support for optimistic Duplicate
+	  Address Detection.  It allows for autoconfigured addresses
+	  to be used more quickly.
+
+	  If unsure, say N.
+
 config INET6_AH
 	tristate "IPv6: AH transformation"
 	depends on IPV6
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index e385469..c884eeb 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -594,6 +594,16 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
 
 	ifa->rt = rt;
 
+	/*
+	 * 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 (rt->rt6i_nexthop == NULL)
+		ifa->flags &= ~IFA_F_OPTIMISTIC;
+
 	ifa->idev = idev;
 	in6_dev_hold(idev);
 	/* For caller */
@@ -770,6 +780,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
 	int tmp_plen;
 	int ret = 0;
 	int max_addresses;
+	u32 addr_flags;
 
 	write_lock(&idev->lock);
 	if (ift) {
@@ -827,10 +838,17 @@ retry:
 	spin_unlock_bh(&ifp->lock);
 
 	write_unlock(&idev->lock);
+
+	addr_flags = IFA_F_TEMPORARY;
+	/* set in addrconf_prefix_rcv() */
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addr_flags |= IFA_F_OPTIMISTIC;
+
 	ift = !max_addresses ||
 	      ipv6_count_addresses(idev) < max_addresses ? 
 		ipv6_add_addr(idev, &addr, tmp_plen,
-			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
+			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
+			      addr_flags) : NULL;
 	if (!ift || IS_ERR(ift)) {
 		in6_ifa_put(ifp);
 		in6_dev_put(idev);
@@ -962,13 +980,14 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
 			 * - Tentative Address (RFC2462 section 5.4)
 			 *  - A tentative address is not considered
 			 *    "assigned to an interface" in the traditional
-			 *    sense.
+			 *    sense, unless it is also flagged as optimistic.
 			 * - Candidate Source Address (section 4)
 			 *  - In any case, anycast addresses, multicast
 			 *    addresses, and the unspecified address MUST
 			 *    NOT be included in a candidate set.
 			 */
-			if (ifa->flags & IFA_F_TENTATIVE)
+			if ((ifa->flags & IFA_F_TENTATIVE) &&
+			    (!(ifa->flags & IFA_F_OPTIMISTIC)))
 				continue;
 			if (unlikely(score.addr_type == IPV6_ADDR_ANY ||
 				     score.addr_type & IPV6_ADDR_MULTICAST)) {
@@ -1027,15 +1046,17 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
 				}
 			}
 
-			/* Rule 3: Avoid deprecated address */
+			/* Rule 3: Avoid deprecated and optimistic addresses */
 			if (hiscore.rule < 3) {
 				if (ipv6_saddr_preferred(hiscore.addr_type) ||
-				    !(ifa_result->flags & IFA_F_DEPRECATED))
+				   (((ifa_result->flags &
+				    (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0)))
 					hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				hiscore.rule++;
 			}
 			if (ipv6_saddr_preferred(score.addr_type) ||
-			    !(ifa->flags & IFA_F_DEPRECATED)) {
+			   (((ifa_result->flags &
+			    (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0))) {
 				score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
 					score.rule = 3;
@@ -1174,7 +1195,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
 }
 
 
-int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
+int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, 
+		    unsigned char banned_flags)
 {
 	struct inet6_dev *idev;
 	int err = -EADDRNOTAVAIL;
@@ -1185,7 +1207,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
 
 		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 && !(ifp->flags & banned_flags)) {
 				ipv6_addr_copy(addr, &ifp->addr);
 				err = 0;
 				break;
@@ -1735,6 +1757,13 @@ ok:
 
 		if (ifp == NULL && 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 &&
+			    !ipv6_devconf.forwarding)
+				addr_flags = IFA_F_OPTIMISTIC;
+#endif
 
 			/* Do not allow to create too much of autoconfigured
 			 * addresses; this would be too easy way to crash kernel.
@@ -1742,7 +1771,8 @@ ok:
 			if (!max_addresses ||
 			    ipv6_count_addresses(in6_dev) < max_addresses)
 				ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len,
-						    addr_type&IPV6_ADDR_SCOPE_MASK, 0);
+						    addr_type&IPV6_ADDR_SCOPE_MASK,
+						    addr_flags);
 
 			if (!ifp || IS_ERR(ifp)) {
 				in6_dev_put(in6_dev);
@@ -1945,7 +1975,11 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
 		ifp->prefered_lft = prefered_lft;
 		ifp->tstamp = jiffies;
 		spin_unlock_bh(&ifp->lock);
-
+		/*
+		 * Note that section 3.1 of RFC 4429 indicates
+		 * that the Optimistic flag should not be set for
+		 * manually configured addresses
+		 */
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
 		addrconf_verify(0);
@@ -2122,8 +2156,16 @@ static void init_loopback(struct net_device *dev)
 static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr)
 {
 	struct inet6_ifaddr * ifp;
+	u32 addr_flags = IFA_F_PERMANENT;
 
-	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	if (idev->cnf.optimistic_dad &&
+	    !ipv6_devconf.forwarding)
+		addr_flags |= IFA_F_OPTIMISTIC;
+#endif
+
+
+	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, addr_flags);
 	if (!IS_ERR(ifp)) {
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
@@ -2190,7 +2232,7 @@ ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
 {
 	struct in6_addr lladdr;
 
-	if (!ipv6_get_lladdr(link_dev, &lladdr)) {
+	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
 		addrconf_add_linklocal(idev, &lladdr);
 		return 0;
 	}
@@ -2527,7 +2569,11 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp)
 	unsigned long rand_num;
 	struct inet6_dev *idev = ifp->idev;
 
-	rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		rand_num = 0;
+	else
+		rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+
 	ifp->probes = idev->cnf.dad_transmits;
 	addrconf_mod_timer(ifp, AC_DAD, rand_num);
 }
@@ -2553,7 +2599,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
 	    !(ifp->flags&IFA_F_TENTATIVE) ||
 	    ifp->flags & IFA_F_NODAD) {
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -2597,7 +2643,7 @@ static void addrconf_dad_timer(unsigned long data)
 		 * DAD was successful
 		 */
 
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -3398,6 +3444,9 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
 #endif
 #endif
 	array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad;
+#endif
 }
 
 static inline size_t inet6_if_nlmsg_size(void)
@@ -3917,6 +3966,17 @@ static struct addrconf_sysctl_table
 			.mode		=	0644,
 			.proc_handler	=	&proc_dointvec,
 		},
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+		{
+			.ctl_name	=	NET_IPV6_OPTIMISTIC_DAD,
+			.procname       =       "optimistic_dad",
+			.data           =       &ipv6_devconf.optimistic_dad,
+			.maxlen         =       sizeof(int),
+			.mode           =       0644,
+			.proc_handler   =       &proc_dointvec,
+
+		},
+#endif
 		{
 			.ctl_name	=	0,	/* sentinel */
 		}
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 7b7bd44..df13fc8 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -861,6 +861,41 @@ static int ip6_dst_lookup_tail(struct sock *sk,
 			goto out_err_release;
 	}
 
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+		/*
+		 * Here if the dst entry we've looked up 
+		 * has a neighbour entry that is in the INCOMPLETE
+		 * state and the src address from the flow is 
+		 * marked as OPTIMISTIC, we release the found 
+		 * dst entry and replace it instead with the 
+		 * dst entry of the nexthop router
+		 */
+		if (!((*dst)->neighbour->nud_state & NUD_VALID)) {
+			struct inet6_ifaddr *ifp;
+			struct flowi fl_gw;
+			int redirect;
+
+			ifp = ipv6_get_ifaddr(&fl->fl6_src, (*dst)->dev, 1);
+
+			redirect = (ifp && ifp->flags & IFA_F_OPTIMISTIC);
+			if (ifp)
+				in6_ifa_put(ifp);
+
+			if (redirect) {
+				/*
+				 * We need to get the dst entry for the 
+				 * default router instead
+				 */
+				dst_release(*dst);
+				memcpy(&fl_gw, fl, sizeof(struct flowi));
+				memset(&fl_gw.fl6_dst, 0, sizeof(struct in6_addr));
+				*dst = ip6_route_output(sk, &fl_gw);
+				if ((err = (*dst)->error))
+					goto out_err_release;				
+			}
+		}
+#endif
+
 	return 0;
 
 out_err_release:
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 882cde4..9c5273c 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
@@ -1789,7 +1789,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address 
 		 * when a valid link-local address is not available.
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 39bb658..98913c4 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -447,6 +447,8 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
 	ifp = ipv6_get_ifaddr(solicited_addr, dev, 1);
  	if (ifp) {
 		src_addr = solicited_addr;
+		if (ifp->flags & IFA_F_OPTIMISTIC)
+			override = 0;
 		in6_ifa_put(ifp);
 	} else {
 		if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr))
@@ -542,7 +544,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
 	int send_llinfo;
 
 	if (saddr == NULL) {
-		if (ipv6_get_lladdr(dev, &addr_buf))
+		if (ipv6_get_lladdr(dev, &addr_buf, 
+				   (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
 			return;
 		saddr = &addr_buf;
 	}
@@ -622,9 +625,33 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
         struct sk_buff *skb;
         struct icmp6hdr *hdr;
 	__u8 * opt;
+	struct inet6_ifaddr *ifp;
+	int send_sllao = dev->addr_len;
         int len;
 	int err;
 
+
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	/*
+	 * According to section 2.2 of RFC 4429, we must not 
+	 * send router solicitations with a sllao from 
+	 * optimistic addresses, but we may send the solicitation
+	 * if we don't include the sllao.  So here we check
+	 * if our address is optimistic, and if so, we
+	 * supress the inclusion of the sllao.
+	 */ 
+	if (send_sllao) {
+		ifp = ipv6_get_ifaddr(saddr, dev, 1);
+		if (ifp) {
+			if (ifp->flags & IFA_F_OPTIMISTIC)  {
+				send_sllao=0;
+				in6_ifa_put(ifp);
+			}
+		} else {
+			send_sllao = 0;
+		}
+	}
+#endif
 	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
 			dev->ifindex);
 
@@ -637,7 +664,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
 		return;
 
 	len = sizeof(struct icmp6hdr);
-	if (dev->addr_len)
+	if (send_sllao)
 		len += ndisc_opt_addr_space(dev);
 
         skb = sock_alloc_send_skb(sk,
@@ -664,7 +691,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
 
 	opt = (u8*) (hdr + 1);
 
-	if (dev->addr_len)
+	if (send_sllao)
 		ndisc_fill_addr_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr,
 				       dev->addr_len, dev->type);
 
@@ -796,28 +823,39 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	inc = ipv6_addr_is_multicast(daddr);
 
 	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
-		if (ifp->flags & IFA_F_TENTATIVE) {
-			/* Address is tentative. If the source
-			   is unspecified address, it is someone
-			   does DAD, otherwise we ignore solicitations
-			   until DAD timer expires.
-			 */
-			if (!dad)
+
+		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
+			if (dad) {
+				if (dev->type == ARPHRD_IEEE802_TR) {
+					unsigned char *sadr = skb->mac.raw;
+					if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
+					    sadr[9] == dev->dev_addr[1] &&
+					    sadr[10] == dev->dev_addr[2] &&
+					    sadr[11] == dev->dev_addr[3] &&
+					    sadr[12] == dev->dev_addr[4] &&
+					    sadr[13] == dev->dev_addr[5]) {
+						/* looped-back to us */
+						goto out;
+					}
+				}
+
+				/*
+				 * We are colliding with another node
+				 * who is doing DAD
+				 * so fail our DAD process
+				 */
+				addrconf_dad_failure(ifp);
 				goto out;
-			if (dev->type == ARPHRD_IEEE802_TR) {
-				unsigned char *sadr = skb->mac.raw;
-				if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
-				    sadr[9] == dev->dev_addr[1] &&
-				    sadr[10] == dev->dev_addr[2] &&
-				    sadr[11] == dev->dev_addr[3] &&
-				    sadr[12] == dev->dev_addr[4] &&
-				    sadr[13] == dev->dev_addr[5]) {
-					/* looped-back to us */
+			} else {
+				/*
+				 * This is not a dad solicitation.
+				 * If we are an optimistic node, 
+				 * we should respond.  
+				 * Otherwise, we should ignore it. 
+				 */
+				if (!(ifp->flags & IFA_F_OPTIMISTIC))
 					goto out;
-				}
 			}
-			addrconf_dad_failure(ifp); 
-			return;
 		}
 
 		idev = ifp->idev;
@@ -1406,7 +1444,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
 
 	dev = skb->dev;
 
-	if (ipv6_get_lladdr(dev, &saddr_buf)) {
+	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
 		ND_PRINTK2(KERN_WARNING
 			   "ICMPv6 Redirect: no link-local address on %s\n",
 			   dev->name);

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-08 19:32                                                               ` Neil Horman
@ 2007-02-12 21:20                                                                 ` Neil Horman
  2007-02-13 20:45                                                                   ` Neil Horman
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-02-12 21:20 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber, netdev

> 
> New patch attached with these fixes included
> 
> 
Ping? Any further thoughts here?  My (allbeit limited) testing seems to be going
fairly well so far...
Neil

> Thanks & Regards
> Neil
> 
> Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
> 
> 
>  include/linux/if_addr.h |    1 
>  include/linux/ipv6.h    |    4 ++
>  include/linux/sysctl.h  |    1 
>  include/net/addrconf.h  |    4 +-
>  net/ipv6/Kconfig        |   10 +++++
>  net/ipv6/addrconf.c     |   90 ++++++++++++++++++++++++++++++++++++++++--------
>  net/ipv6/ip6_output.c   |   35 ++++++++++++++++++
>  net/ipv6/mcast.c        |    4 +-
>  net/ipv6/ndisc.c        |   84 ++++++++++++++++++++++++++++++++------------
>  9 files changed, 192 insertions(+), 41 deletions(-)
> 
> 
> 
> 
> diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
> index d557e4c..43f3bed 100644
> --- a/include/linux/if_addr.h
> +++ b/include/linux/if_addr.h
> @@ -39,6 +39,7 @@ enum
>  #define IFA_F_TEMPORARY		IFA_F_SECONDARY
>  
>  #define	IFA_F_NODAD		0x02
> +#define IFA_F_OPTIMISTIC	0x04
>  #define	IFA_F_HOMEADDRESS	0x10
>  #define IFA_F_DEPRECATED	0x20
>  #define IFA_F_TENTATIVE		0x40
> diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
> index f824113..bf93c1b 100644
> --- a/include/linux/ipv6.h
> +++ b/include/linux/ipv6.h
> @@ -177,6 +177,9 @@ struct ipv6_devconf {
>  #endif
>  #endif
>  	__s32		proxy_ndp;
> +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> +	__s32		optimistic_dad;
> +#endif
>  	void		*sysctl;
>  };
>  
> @@ -205,6 +208,7 @@ enum {
>  	DEVCONF_RTR_PROBE_INTERVAL,
>  	DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN,
>  	DEVCONF_PROXY_NDP,
> +	DEVCONF_OPTIMISTIC_DAD,
>  	DEVCONF_MAX
>  };
>  
> diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
> index 81480e6..972a33a 100644
> --- a/include/linux/sysctl.h
> +++ b/include/linux/sysctl.h
> @@ -570,6 +570,7 @@ enum {
>  	NET_IPV6_RTR_PROBE_INTERVAL=21,
>  	NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22,
>  	NET_IPV6_PROXY_NDP=23,
> +	NET_IPV6_OPTIMISTIC_DAD=24,
>  	__NET_IPV6_MAX
>  };
>  
> diff --git a/include/net/addrconf.h b/include/net/addrconf.h
> index 88df8fc..d248a19 100644
> --- a/include/net/addrconf.h
> +++ b/include/net/addrconf.h
> @@ -73,7 +73,9 @@ extern int			ipv6_get_saddr(struct dst_entry *dst,
>  extern int			ipv6_dev_get_saddr(struct net_device *dev, 
>  					       struct in6_addr *daddr,
>  					       struct in6_addr *saddr);
> -extern int			ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
> +extern int			ipv6_get_lladdr(struct net_device *dev, 
> +						struct in6_addr *,
> +						unsigned char banned_flags);
>  extern int			ipv6_rcv_saddr_equal(const struct sock *sk, 
>  						      const struct sock *sk2);
>  extern void			addrconf_join_solict(struct net_device *dev,
> diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
> index deb4101..822d3eb 100644
> --- a/net/ipv6/Kconfig
> +++ b/net/ipv6/Kconfig
> @@ -57,6 +57,16 @@ config IPV6_ROUTE_INFO
>  
>  	  If unsure, say N.
>  
> +config IPV6_OPTIMISTIC_DAD
> +	bool "IPv6: Enable RFC 4429 Optimistic DAD (EXPERIMENTAL)"
> +	depends on IPV6 && EXPERIMENTAL
> +	---help---
> +	  This is experimental support for optimistic Duplicate
> +	  Address Detection.  It allows for autoconfigured addresses
> +	  to be used more quickly.
> +
> +	  If unsure, say N.
> +
>  config INET6_AH
>  	tristate "IPv6: AH transformation"
>  	depends on IPV6
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index e385469..c884eeb 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -594,6 +594,16 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
>  
>  	ifa->rt = rt;
>  
> +	/*
> +	 * 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 (rt->rt6i_nexthop == NULL)
> +		ifa->flags &= ~IFA_F_OPTIMISTIC;
> +
>  	ifa->idev = idev;
>  	in6_dev_hold(idev);
>  	/* For caller */
> @@ -770,6 +780,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
>  	int tmp_plen;
>  	int ret = 0;
>  	int max_addresses;
> +	u32 addr_flags;
>  
>  	write_lock(&idev->lock);
>  	if (ift) {
> @@ -827,10 +838,17 @@ retry:
>  	spin_unlock_bh(&ifp->lock);
>  
>  	write_unlock(&idev->lock);
> +
> +	addr_flags = IFA_F_TEMPORARY;
> +	/* set in addrconf_prefix_rcv() */
> +	if (ifp->flags & IFA_F_OPTIMISTIC)
> +		addr_flags |= IFA_F_OPTIMISTIC;
> +
>  	ift = !max_addresses ||
>  	      ipv6_count_addresses(idev) < max_addresses ? 
>  		ipv6_add_addr(idev, &addr, tmp_plen,
> -			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
> +			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
> +			      addr_flags) : NULL;
>  	if (!ift || IS_ERR(ift)) {
>  		in6_ifa_put(ifp);
>  		in6_dev_put(idev);
> @@ -962,13 +980,14 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
>  			 * - Tentative Address (RFC2462 section 5.4)
>  			 *  - A tentative address is not considered
>  			 *    "assigned to an interface" in the traditional
> -			 *    sense.
> +			 *    sense, unless it is also flagged as optimistic.
>  			 * - Candidate Source Address (section 4)
>  			 *  - In any case, anycast addresses, multicast
>  			 *    addresses, and the unspecified address MUST
>  			 *    NOT be included in a candidate set.
>  			 */
> -			if (ifa->flags & IFA_F_TENTATIVE)
> +			if ((ifa->flags & IFA_F_TENTATIVE) &&
> +			    (!(ifa->flags & IFA_F_OPTIMISTIC)))
>  				continue;
>  			if (unlikely(score.addr_type == IPV6_ADDR_ANY ||
>  				     score.addr_type & IPV6_ADDR_MULTICAST)) {
> @@ -1027,15 +1046,17 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
>  				}
>  			}
>  
> -			/* Rule 3: Avoid deprecated address */
> +			/* Rule 3: Avoid deprecated and optimistic addresses */
>  			if (hiscore.rule < 3) {
>  				if (ipv6_saddr_preferred(hiscore.addr_type) ||
> -				    !(ifa_result->flags & IFA_F_DEPRECATED))
> +				   (((ifa_result->flags &
> +				    (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0)))
>  					hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
>  				hiscore.rule++;
>  			}
>  			if (ipv6_saddr_preferred(score.addr_type) ||
> -			    !(ifa->flags & IFA_F_DEPRECATED)) {
> +			   (((ifa_result->flags &
> +			    (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0))) {
>  				score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
>  				if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
>  					score.rule = 3;
> @@ -1174,7 +1195,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
>  }
>  
>  
> -int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
> +int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, 
> +		    unsigned char banned_flags)
>  {
>  	struct inet6_dev *idev;
>  	int err = -EADDRNOTAVAIL;
> @@ -1185,7 +1207,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
>  
>  		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 && !(ifp->flags & banned_flags)) {
>  				ipv6_addr_copy(addr, &ifp->addr);
>  				err = 0;
>  				break;
> @@ -1735,6 +1757,13 @@ ok:
>  
>  		if (ifp == NULL && 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 &&
> +			    !ipv6_devconf.forwarding)
> +				addr_flags = IFA_F_OPTIMISTIC;
> +#endif
>  
>  			/* Do not allow to create too much of autoconfigured
>  			 * addresses; this would be too easy way to crash kernel.
> @@ -1742,7 +1771,8 @@ ok:
>  			if (!max_addresses ||
>  			    ipv6_count_addresses(in6_dev) < max_addresses)
>  				ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len,
> -						    addr_type&IPV6_ADDR_SCOPE_MASK, 0);
> +						    addr_type&IPV6_ADDR_SCOPE_MASK,
> +						    addr_flags);
>  
>  			if (!ifp || IS_ERR(ifp)) {
>  				in6_dev_put(in6_dev);
> @@ -1945,7 +1975,11 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
>  		ifp->prefered_lft = prefered_lft;
>  		ifp->tstamp = jiffies;
>  		spin_unlock_bh(&ifp->lock);
> -
> +		/*
> +		 * Note that section 3.1 of RFC 4429 indicates
> +		 * that the Optimistic flag should not be set for
> +		 * manually configured addresses
> +		 */
>  		addrconf_dad_start(ifp, 0);
>  		in6_ifa_put(ifp);
>  		addrconf_verify(0);
> @@ -2122,8 +2156,16 @@ static void init_loopback(struct net_device *dev)
>  static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr)
>  {
>  	struct inet6_ifaddr * ifp;
> +	u32 addr_flags = IFA_F_PERMANENT;
>  
> -	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
> +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> +	if (idev->cnf.optimistic_dad &&
> +	    !ipv6_devconf.forwarding)
> +		addr_flags |= IFA_F_OPTIMISTIC;
> +#endif
> +
> +
> +	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, addr_flags);
>  	if (!IS_ERR(ifp)) {
>  		addrconf_dad_start(ifp, 0);
>  		in6_ifa_put(ifp);
> @@ -2190,7 +2232,7 @@ ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
>  {
>  	struct in6_addr lladdr;
>  
> -	if (!ipv6_get_lladdr(link_dev, &lladdr)) {
> +	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
>  		addrconf_add_linklocal(idev, &lladdr);
>  		return 0;
>  	}
> @@ -2527,7 +2569,11 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp)
>  	unsigned long rand_num;
>  	struct inet6_dev *idev = ifp->idev;
>  
> -	rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
> +	if (ifp->flags & IFA_F_OPTIMISTIC)
> +		rand_num = 0;
> +	else
> +		rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
> +
>  	ifp->probes = idev->cnf.dad_transmits;
>  	addrconf_mod_timer(ifp, AC_DAD, rand_num);
>  }
> @@ -2553,7 +2599,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
>  	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
>  	    !(ifp->flags&IFA_F_TENTATIVE) ||
>  	    ifp->flags & IFA_F_NODAD) {
> -		ifp->flags &= ~IFA_F_TENTATIVE;
> +		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
>  		spin_unlock_bh(&ifp->lock);
>  		read_unlock_bh(&idev->lock);
>  
> @@ -2597,7 +2643,7 @@ static void addrconf_dad_timer(unsigned long data)
>  		 * DAD was successful
>  		 */
>  
> -		ifp->flags &= ~IFA_F_TENTATIVE;
> +		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
>  		spin_unlock_bh(&ifp->lock);
>  		read_unlock_bh(&idev->lock);
>  
> @@ -3398,6 +3444,9 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
>  #endif
>  #endif
>  	array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp;
> +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> +	array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad;
> +#endif
>  }
>  
>  static inline size_t inet6_if_nlmsg_size(void)
> @@ -3917,6 +3966,17 @@ static struct addrconf_sysctl_table
>  			.mode		=	0644,
>  			.proc_handler	=	&proc_dointvec,
>  		},
> +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> +		{
> +			.ctl_name	=	NET_IPV6_OPTIMISTIC_DAD,
> +			.procname       =       "optimistic_dad",
> +			.data           =       &ipv6_devconf.optimistic_dad,
> +			.maxlen         =       sizeof(int),
> +			.mode           =       0644,
> +			.proc_handler   =       &proc_dointvec,
> +
> +		},
> +#endif
>  		{
>  			.ctl_name	=	0,	/* sentinel */
>  		}
> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
> index 7b7bd44..df13fc8 100644
> --- a/net/ipv6/ip6_output.c
> +++ b/net/ipv6/ip6_output.c
> @@ -861,6 +861,41 @@ static int ip6_dst_lookup_tail(struct sock *sk,
>  			goto out_err_release;
>  	}
>  
> +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> +		/*
> +		 * Here if the dst entry we've looked up 
> +		 * has a neighbour entry that is in the INCOMPLETE
> +		 * state and the src address from the flow is 
> +		 * marked as OPTIMISTIC, we release the found 
> +		 * dst entry and replace it instead with the 
> +		 * dst entry of the nexthop router
> +		 */
> +		if (!((*dst)->neighbour->nud_state & NUD_VALID)) {
> +			struct inet6_ifaddr *ifp;
> +			struct flowi fl_gw;
> +			int redirect;
> +
> +			ifp = ipv6_get_ifaddr(&fl->fl6_src, (*dst)->dev, 1);
> +
> +			redirect = (ifp && ifp->flags & IFA_F_OPTIMISTIC);
> +			if (ifp)
> +				in6_ifa_put(ifp);
> +
> +			if (redirect) {
> +				/*
> +				 * We need to get the dst entry for the 
> +				 * default router instead
> +				 */
> +				dst_release(*dst);
> +				memcpy(&fl_gw, fl, sizeof(struct flowi));
> +				memset(&fl_gw.fl6_dst, 0, sizeof(struct in6_addr));
> +				*dst = ip6_route_output(sk, &fl_gw);
> +				if ((err = (*dst)->error))
> +					goto out_err_release;				
> +			}
> +		}
> +#endif
> +
>  	return 0;
>  
>  out_err_release:
> diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
> index 882cde4..9c5273c 100644
> --- a/net/ipv6/mcast.c
> +++ b/net/ipv6/mcast.c
> @@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
>  
>  	skb_reserve(skb, LL_RESERVED_SPACE(dev));
>  
> -	if (ipv6_get_lladdr(dev, &addr_buf)) {
> +	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
>  		/* <draft-ietf-magma-mld-source-05.txt>:
>  		 * use unspecified address as the source address 
>  		 * when a valid link-local address is not available.
> @@ -1789,7 +1789,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
>  
>  	skb_reserve(skb, LL_RESERVED_SPACE(dev));
>  
> -	if (ipv6_get_lladdr(dev, &addr_buf)) {
> +	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
>  		/* <draft-ietf-magma-mld-source-05.txt>:
>  		 * use unspecified address as the source address 
>  		 * when a valid link-local address is not available.
> diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
> index 39bb658..98913c4 100644
> --- a/net/ipv6/ndisc.c
> +++ b/net/ipv6/ndisc.c
> @@ -447,6 +447,8 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
>  	ifp = ipv6_get_ifaddr(solicited_addr, dev, 1);
>   	if (ifp) {
>  		src_addr = solicited_addr;
> +		if (ifp->flags & IFA_F_OPTIMISTIC)
> +			override = 0;
>  		in6_ifa_put(ifp);
>  	} else {
>  		if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr))
> @@ -542,7 +544,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
>  	int send_llinfo;
>  
>  	if (saddr == NULL) {
> -		if (ipv6_get_lladdr(dev, &addr_buf))
> +		if (ipv6_get_lladdr(dev, &addr_buf, 
> +				   (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
>  			return;
>  		saddr = &addr_buf;
>  	}
> @@ -622,9 +625,33 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
>          struct sk_buff *skb;
>          struct icmp6hdr *hdr;
>  	__u8 * opt;
> +	struct inet6_ifaddr *ifp;
> +	int send_sllao = dev->addr_len;
>          int len;
>  	int err;
>  
> +
> +#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
> +	/*
> +	 * According to section 2.2 of RFC 4429, we must not 
> +	 * send router solicitations with a sllao from 
> +	 * optimistic addresses, but we may send the solicitation
> +	 * if we don't include the sllao.  So here we check
> +	 * if our address is optimistic, and if so, we
> +	 * supress the inclusion of the sllao.
> +	 */ 
> +	if (send_sllao) {
> +		ifp = ipv6_get_ifaddr(saddr, dev, 1);
> +		if (ifp) {
> +			if (ifp->flags & IFA_F_OPTIMISTIC)  {
> +				send_sllao=0;
> +				in6_ifa_put(ifp);
> +			}
> +		} else {
> +			send_sllao = 0;
> +		}
> +	}
> +#endif
>  	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
>  			dev->ifindex);
>  
> @@ -637,7 +664,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
>  		return;
>  
>  	len = sizeof(struct icmp6hdr);
> -	if (dev->addr_len)
> +	if (send_sllao)
>  		len += ndisc_opt_addr_space(dev);
>  
>          skb = sock_alloc_send_skb(sk,
> @@ -664,7 +691,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
>  
>  	opt = (u8*) (hdr + 1);
>  
> -	if (dev->addr_len)
> +	if (send_sllao)
>  		ndisc_fill_addr_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr,
>  				       dev->addr_len, dev->type);
>  
> @@ -796,28 +823,39 @@ static void ndisc_recv_ns(struct sk_buff *skb)
>  	inc = ipv6_addr_is_multicast(daddr);
>  
>  	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
> -		if (ifp->flags & IFA_F_TENTATIVE) {
> -			/* Address is tentative. If the source
> -			   is unspecified address, it is someone
> -			   does DAD, otherwise we ignore solicitations
> -			   until DAD timer expires.
> -			 */
> -			if (!dad)
> +
> +		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
> +			if (dad) {
> +				if (dev->type == ARPHRD_IEEE802_TR) {
> +					unsigned char *sadr = skb->mac.raw;
> +					if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
> +					    sadr[9] == dev->dev_addr[1] &&
> +					    sadr[10] == dev->dev_addr[2] &&
> +					    sadr[11] == dev->dev_addr[3] &&
> +					    sadr[12] == dev->dev_addr[4] &&
> +					    sadr[13] == dev->dev_addr[5]) {
> +						/* looped-back to us */
> +						goto out;
> +					}
> +				}
> +
> +				/*
> +				 * We are colliding with another node
> +				 * who is doing DAD
> +				 * so fail our DAD process
> +				 */
> +				addrconf_dad_failure(ifp);
>  				goto out;
> -			if (dev->type == ARPHRD_IEEE802_TR) {
> -				unsigned char *sadr = skb->mac.raw;
> -				if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
> -				    sadr[9] == dev->dev_addr[1] &&
> -				    sadr[10] == dev->dev_addr[2] &&
> -				    sadr[11] == dev->dev_addr[3] &&
> -				    sadr[12] == dev->dev_addr[4] &&
> -				    sadr[13] == dev->dev_addr[5]) {
> -					/* looped-back to us */
> +			} else {
> +				/*
> +				 * This is not a dad solicitation.
> +				 * If we are an optimistic node, 
> +				 * we should respond.  
> +				 * Otherwise, we should ignore it. 
> +				 */
> +				if (!(ifp->flags & IFA_F_OPTIMISTIC))
>  					goto out;
> -				}
>  			}
> -			addrconf_dad_failure(ifp); 
> -			return;
>  		}
>  
>  		idev = ifp->idev;
> @@ -1406,7 +1444,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
>  
>  	dev = skb->dev;
>  
> -	if (ipv6_get_lladdr(dev, &saddr_buf)) {
> +	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
>  		ND_PRINTK2(KERN_WARNING
>  			   "ICMPv6 Redirect: no link-local address on %s\n",
>  			   dev->name);
> -
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-08 13:07                                                           ` Neil Horman
@ 2007-02-12 23:27                                                             ` YOSHIFUJI Hideaki / 吉藤英明
  2007-02-13 18:22                                                               ` Neil Horman
  0 siblings, 1 reply; 71+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2007-02-12 23:27 UTC (permalink / raw)
  To: nhorman
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber,
	netdev, yoshfuji

In article <20070208130715.GA27267@hmsreliant.homelinux.net> (at Thu, 8 Feb 2007 08:07:15 -0500), Neil Horman <nhorman@tuxdriver.com> says:

> > I still have a question.  Now, who will install the kernel route for
> > the incoming packet?  Can we get packet for our unicast address during
> > optimistic DAD period?
> > 
> Not sure what you're getting at here.  RFC 4429 makes no distinction between
> optimistic and non-optimistic packets for recevied frames, so routes for
> received packets will be added by the same code that does it currently (which I
> admittedly haven't looked into to closely).  Packets received for our unicast
> address (even if it is optimistic) are handled just as any other packet is (same
> as it is for TENTATIVE addresses, as I understand it). 

I think it is clear.

In tentative state, we do not receive any packet.
After finishind DAD process, we install kernel route for that address
in net/ipv6/addrconf.c:__ipv6_ifa_notify(), and we start receiving
packet for that address.

On the other hand, optimistic addresses are like in deprecated state.
So, we should be able to receive frames for those addresses.

--yoshfuji

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-12 23:27                                                             ` YOSHIFUJI Hideaki / 吉藤英明
@ 2007-02-13 18:22                                                               ` Neil Horman
  0 siblings, 0 replies; 71+ messages in thread
From: Neil Horman @ 2007-02-13 18:22 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber, netdev

On Tue, Feb 13, 2007 at 08:27:59AM +0900, YOSHIFUJI Hideaki / 吉藤英明 wrote:
> In article <20070208130715.GA27267@hmsreliant.homelinux.net> (at Thu, 8 Feb 2007 08:07:15 -0500), Neil Horman <nhorman@tuxdriver.com> says:
> 
> > > I still have a question.  Now, who will install the kernel route for
> > > the incoming packet?  Can we get packet for our unicast address during
> > > optimistic DAD period?
> > > 
> > Not sure what you're getting at here.  RFC 4429 makes no distinction between
> > optimistic and non-optimistic packets for recevied frames, so routes for
> > received packets will be added by the same code that does it currently (which I
> > admittedly haven't looked into to closely).  Packets received for our unicast
> > address (even if it is optimistic) are handled just as any other packet is (same
> > as it is for TENTATIVE addresses, as I understand it). 
> 
> I think it is clear.
> 
> In tentative state, we do not receive any packet.
> After finishind DAD process, we install kernel route for that address
> in net/ipv6/addrconf.c:__ipv6_ifa_notify(), and we start receiving
> packet for that address.
> 
> On the other hand, optimistic addresses are like in deprecated state.
> So, we should be able to receive frames for those addresses.
> 
I'm sorry, you're right, it is clear now.  I hadn't considered the need for a
route to receive frames, and the point at which we insert it.  I'm puting
together a new patch for this right now and will post it as soon as I've built
and started testing it.

Thanks & Regards
Neil


> --yoshfuji
> -
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-12 21:20                                                                 ` Neil Horman
@ 2007-02-13 20:45                                                                   ` Neil Horman
  2007-02-13 21:46                                                                     ` YOSHIFUJI Hideaki / 吉藤英明
  0 siblings, 1 reply; 71+ messages in thread
From: Neil Horman @ 2007-02-13 20:45 UTC (permalink / raw)
  To: YOSHIFUJI Hideaki / 吉藤英明
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber, netdev


>I still have a question.  Now, who will install the kernel route for
> > the incoming packet?  Can we get packet for our unicast address during
> > optimistic DAD period?

As per Yoshifjui's observation, the current patch doesn't currently allow for
the reception of inbound packets to our optimistic address, since we don't add a
route for this destination address to our table until we call ipv6_ifa_notify,
which happens after the dad process completes.  This updated patch corrects that
by adding a route for the optimistic address in addrconf_dad_start, and only
conditionally doing it in ipv6_ifa_notify if the route has not already been
added.  Route deltion should still be common with non-optimistic cases since the
route is removed on interface deletion from ipv6_ifa_notify, which occurs in the
common code path ipv6_del_addr.

Thanks & Regards
Neil

Signed-off-by: Neil Horman <nhorman@tuxdriver.com>


 include/linux/if_addr.h |    1 
 include/linux/ipv6.h    |    4 +
 include/linux/sysctl.h  |    1 
 include/net/addrconf.h  |    4 +
 net/ipv6/Kconfig        |   10 ++++
 net/ipv6/addrconf.c     |  107 ++++++++++++++++++++++++++++++++++++++++--------
 net/ipv6/ip6_output.c   |   35 +++++++++++++++
 net/ipv6/mcast.c        |    4 -
 net/ipv6/ndisc.c        |   86 +++++++++++++++++++++++++++-----------
 9 files changed, 209 insertions(+), 43 deletions(-)



diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
index d557e4c..43f3bed 100644
--- a/include/linux/if_addr.h
+++ b/include/linux/if_addr.h
@@ -39,6 +39,7 @@ enum
 #define IFA_F_TEMPORARY		IFA_F_SECONDARY
 
 #define	IFA_F_NODAD		0x02
+#define IFA_F_OPTIMISTIC	0x04
 #define	IFA_F_HOMEADDRESS	0x10
 #define IFA_F_DEPRECATED	0x20
 #define IFA_F_TENTATIVE		0x40
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index f824113..bf93c1b 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -177,6 +177,9 @@ struct ipv6_devconf {
 #endif
 #endif
 	__s32		proxy_ndp;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	__s32		optimistic_dad;
+#endif
 	void		*sysctl;
 };
 
@@ -205,6 +208,7 @@ enum {
 	DEVCONF_RTR_PROBE_INTERVAL,
 	DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN,
 	DEVCONF_PROXY_NDP,
+	DEVCONF_OPTIMISTIC_DAD,
 	DEVCONF_MAX
 };
 
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 665412c..a7c0496 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -570,6 +570,7 @@ enum {
 	NET_IPV6_RTR_PROBE_INTERVAL=21,
 	NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22,
 	NET_IPV6_PROXY_NDP=23,
+	NET_IPV6_OPTIMISTIC_DAD=24,
 	__NET_IPV6_MAX
 };
 
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 88df8fc..d248a19 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -73,7 +73,9 @@ extern int			ipv6_get_saddr(struct dst_entry *dst,
 extern int			ipv6_dev_get_saddr(struct net_device *dev, 
 					       struct in6_addr *daddr,
 					       struct in6_addr *saddr);
-extern int			ipv6_get_lladdr(struct net_device *dev, struct in6_addr *);
+extern int			ipv6_get_lladdr(struct net_device *dev, 
+						struct in6_addr *,
+						unsigned char banned_flags);
 extern int			ipv6_rcv_saddr_equal(const struct sock *sk, 
 						      const struct sock *sk2);
 extern void			addrconf_join_solict(struct net_device *dev,
diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig
index deb4101..822d3eb 100644
--- a/net/ipv6/Kconfig
+++ b/net/ipv6/Kconfig
@@ -57,6 +57,16 @@ config IPV6_ROUTE_INFO
 
 	  If unsure, say N.
 
+config IPV6_OPTIMISTIC_DAD
+	bool "IPv6: Enable RFC 4429 Optimistic DAD (EXPERIMENTAL)"
+	depends on IPV6 && EXPERIMENTAL
+	---help---
+	  This is experimental support for optimistic Duplicate
+	  Address Detection.  It allows for autoconfigured addresses
+	  to be used more quickly.
+
+	  If unsure, say N.
+
 config INET6_AH
 	tristate "IPv6: AH transformation"
 	depends on IPV6
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index ea0755b..7ea0a36 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -594,6 +594,16 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
 
 	ifa->rt = rt;
 
+	/*
+	 * 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 (rt->rt6i_nexthop == NULL)
+		ifa->flags &= ~IFA_F_OPTIMISTIC;
+
 	ifa->idev = idev;
 	in6_dev_hold(idev);
 	/* For caller */
@@ -770,6 +780,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
 	int tmp_plen;
 	int ret = 0;
 	int max_addresses;
+	u32 addr_flags;
 
 	write_lock(&idev->lock);
 	if (ift) {
@@ -827,10 +838,17 @@ retry:
 	spin_unlock_bh(&ifp->lock);
 
 	write_unlock(&idev->lock);
+
+	addr_flags = IFA_F_TEMPORARY;
+	/* set in addrconf_prefix_rcv() */
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		addr_flags |= IFA_F_OPTIMISTIC;
+
 	ift = !max_addresses ||
 	      ipv6_count_addresses(idev) < max_addresses ?
 		ipv6_add_addr(idev, &addr, tmp_plen,
-			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, IFA_F_TEMPORARY) : NULL;
+			      ipv6_addr_type(&addr)&IPV6_ADDR_SCOPE_MASK, 
+			      addr_flags) : NULL;
 	if (!ift || IS_ERR(ift)) {
 		in6_ifa_put(ifp);
 		in6_dev_put(idev);
@@ -962,13 +980,14 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
 			 * - Tentative Address (RFC2462 section 5.4)
 			 *  - A tentative address is not considered
 			 *    "assigned to an interface" in the traditional
-			 *    sense.
+			 *    sense, unless it is also flagged as optimistic.
 			 * - Candidate Source Address (section 4)
 			 *  - In any case, anycast addresses, multicast
 			 *    addresses, and the unspecified address MUST
 			 *    NOT be included in a candidate set.
 			 */
-			if (ifa->flags & IFA_F_TENTATIVE)
+			if ((ifa->flags & IFA_F_TENTATIVE) &&
+			    (!(ifa->flags & IFA_F_OPTIMISTIC)))
 				continue;
 			if (unlikely(score.addr_type == IPV6_ADDR_ANY ||
 				     score.addr_type & IPV6_ADDR_MULTICAST)) {
@@ -1027,15 +1046,17 @@ int ipv6_dev_get_saddr(struct net_device *daddr_dev,
 				}
 			}
 
-			/* Rule 3: Avoid deprecated address */
+			/* Rule 3: Avoid deprecated and optimistic addresses */
 			if (hiscore.rule < 3) {
 				if (ipv6_saddr_preferred(hiscore.addr_type) ||
-				    !(ifa_result->flags & IFA_F_DEPRECATED))
+				   (((ifa_result->flags &
+				    (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0)))
 					hiscore.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				hiscore.rule++;
 			}
 			if (ipv6_saddr_preferred(score.addr_type) ||
-			    !(ifa->flags & IFA_F_DEPRECATED)) {
+			   (((ifa_result->flags &
+			    (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC)) == 0))) {
 				score.attrs |= IPV6_SADDR_SCORE_PREFERRED;
 				if (!(hiscore.attrs & IPV6_SADDR_SCORE_PREFERRED)) {
 					score.rule = 3;
@@ -1174,7 +1195,8 @@ int ipv6_get_saddr(struct dst_entry *dst,
 }
 
 
-int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
+int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, 
+		    unsigned char banned_flags)
 {
 	struct inet6_dev *idev;
 	int err = -EADDRNOTAVAIL;
@@ -1185,7 +1207,7 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
 
 		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 && !(ifp->flags & banned_flags)) {
 				ipv6_addr_copy(addr, &ifp->addr);
 				err = 0;
 				break;
@@ -1735,6 +1757,13 @@ ok:
 
 		if (ifp == NULL && 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 &&
+			    !ipv6_devconf.forwarding)
+				addr_flags = IFA_F_OPTIMISTIC;
+#endif
 
 			/* Do not allow to create too much of autoconfigured
 			 * addresses; this would be too easy way to crash kernel.
@@ -1742,7 +1771,8 @@ ok:
 			if (!max_addresses ||
 			    ipv6_count_addresses(in6_dev) < max_addresses)
 				ifp = ipv6_add_addr(in6_dev, &addr, pinfo->prefix_len,
-						    addr_type&IPV6_ADDR_SCOPE_MASK, 0);
+						    addr_type&IPV6_ADDR_SCOPE_MASK,
+						    addr_flags);
 
 			if (!ifp || IS_ERR(ifp)) {
 				in6_dev_put(in6_dev);
@@ -1945,7 +1975,11 @@ static int inet6_addr_add(int ifindex, struct in6_addr *pfx, int plen,
 		ifp->prefered_lft = prefered_lft;
 		ifp->tstamp = jiffies;
 		spin_unlock_bh(&ifp->lock);
-
+		/*
+		 * Note that section 3.1 of RFC 4429 indicates
+		 * that the Optimistic flag should not be set for
+		 * manually configured addresses
+		 */
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
 		addrconf_verify(0);
@@ -2122,8 +2156,16 @@ static void init_loopback(struct net_device *dev)
 static void addrconf_add_linklocal(struct inet6_dev *idev, struct in6_addr *addr)
 {
 	struct inet6_ifaddr * ifp;
+	u32 addr_flags = IFA_F_PERMANENT;
+
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	if (idev->cnf.optimistic_dad &&
+	    !ipv6_devconf.forwarding)
+		addr_flags |= IFA_F_OPTIMISTIC;
+#endif
+
 
-	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
+	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, addr_flags);
 	if (!IS_ERR(ifp)) {
 		addrconf_dad_start(ifp, 0);
 		in6_ifa_put(ifp);
@@ -2190,7 +2232,7 @@ ipv6_inherit_linklocal(struct inet6_dev *idev, struct net_device *link_dev)
 {
 	struct in6_addr lladdr;
 
-	if (!ipv6_get_lladdr(link_dev, &lladdr)) {
+	if (!ipv6_get_lladdr(link_dev, &lladdr, IFA_F_TENTATIVE)) {
 		addrconf_add_linklocal(idev, &lladdr);
 		return 0;
 	}
@@ -2527,7 +2569,11 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp)
 	unsigned long rand_num;
 	struct inet6_dev *idev = ifp->idev;
 
-	rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+	if (ifp->flags & IFA_F_OPTIMISTIC)
+		rand_num = 0;
+	else
+		rand_num = net_random() % (idev->cnf.rtr_solicit_delay ? : 1);
+
 	ifp->probes = idev->cnf.dad_transmits;
 	addrconf_mod_timer(ifp, AC_DAD, rand_num);
 }
@@ -2553,7 +2599,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 	if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
 	    !(ifp->flags&IFA_F_TENTATIVE) ||
 	    ifp->flags & IFA_F_NODAD) {
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -2573,6 +2619,14 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
 		addrconf_dad_stop(ifp);
 		return;
 	}
+
+	/*
+	 * Optimistic nodes can start receiving 
+	 * Frames right away
+	 */
+	if(ifp->flags & IFA_F_OPTIMISTIC)
+		ip6_ins_rt(ifp->rt);
+
 	addrconf_dad_kick(ifp);
 	spin_unlock_bh(&ifp->lock);
 out:
@@ -2597,7 +2651,7 @@ static void addrconf_dad_timer(unsigned long data)
 		 * DAD was successful
 		 */
 
-		ifp->flags &= ~IFA_F_TENTATIVE;
+		ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
 		spin_unlock_bh(&ifp->lock);
 		read_unlock_bh(&idev->lock);
 
@@ -3410,6 +3464,9 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
 #endif
 #endif
 	array[DEVCONF_PROXY_NDP] = cnf->proxy_ndp;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad;
+#endif
 }
 
 static inline size_t inet6_if_nlmsg_size(void)
@@ -3624,7 +3681,14 @@ static void __ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp)
 
 	switch (event) {
 	case RTM_NEWADDR:
-		ip6_ins_rt(ifp->rt);
+		/*
+		 * If the address was optimistic
+		 * we inserted the route at the start of
+		 * our DAD process, so we don't need
+		 * to do it again
+		 */
+		if (!(ifp->rt->rt6i_node))
+			ip6_ins_rt(ifp->rt);
 		if (ifp->idev->cnf.forwarding)
 			addrconf_join_anycast(ifp);
 		break;
@@ -3937,6 +4001,17 @@ static struct addrconf_sysctl_table
 			.mode		=	0644,
 			.proc_handler	=	&proc_dointvec,
 		},
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+		{
+			.ctl_name	=	NET_IPV6_OPTIMISTIC_DAD,
+			.procname       =       "optimistic_dad",
+			.data           =       &ipv6_devconf.optimistic_dad,
+			.maxlen         =       sizeof(int),
+			.mode           =       0644,
+			.proc_handler   =       &proc_dointvec,
+
+		},
+#endif
 		{
 			.ctl_name	=	0,	/* sentinel */
 		}
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 3055169..74b853b 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -861,6 +861,41 @@ static int ip6_dst_lookup_tail(struct sock *sk,
 			goto out_err_release;
 	}
 
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+		/*
+		 * Here if the dst entry we've looked up 
+		 * has a neighbour entry that is in the INCOMPLETE
+		 * state and the src address from the flow is 
+		 * marked as OPTIMISTIC, we release the found 
+		 * dst entry and replace it instead with the 
+		 * dst entry of the nexthop router
+		 */
+		if (!((*dst)->neighbour->nud_state & NUD_VALID)) {
+			struct inet6_ifaddr *ifp;
+			struct flowi fl_gw;
+			int redirect;
+
+			ifp = ipv6_get_ifaddr(&fl->fl6_src, (*dst)->dev, 1);
+
+			redirect = (ifp && ifp->flags & IFA_F_OPTIMISTIC);
+			if (ifp)
+				in6_ifa_put(ifp);
+
+			if (redirect) {
+				/*
+				 * We need to get the dst entry for the 
+				 * default router instead
+				 */
+				dst_release(*dst);
+				memcpy(&fl_gw, fl, sizeof(struct flowi));
+				memset(&fl_gw.fl6_dst, 0, sizeof(struct in6_addr));
+				*dst = ip6_route_output(sk, &fl_gw);
+				if ((err = (*dst)->error))
+					goto out_err_release;				
+			}
+		}
+#endif
+
 	return 0;
 
 out_err_release:
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index a8d6625..924e249 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1411,7 +1411,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address
 		 * when a valid link-local address is not available.
@@ -1791,7 +1791,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
 
 	skb_reserve(skb, LL_RESERVED_SPACE(dev));
 
-	if (ipv6_get_lladdr(dev, &addr_buf)) {
+	if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) {
 		/* <draft-ietf-magma-mld-source-05.txt>:
 		 * use unspecified address as the source address
 		 * when a valid link-local address is not available.
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 121f31c..8ec822b 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -447,6 +447,8 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
 	ifp = ipv6_get_ifaddr(solicited_addr, dev, 1);
 	if (ifp) {
 		src_addr = solicited_addr;
+		if (ifp->flags & IFA_F_OPTIMISTIC)
+			override = 0;
 		in6_ifa_put(ifp);
 	} else {
 		if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr))
@@ -542,7 +544,8 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
 	int send_llinfo;
 
 	if (saddr == NULL) {
-		if (ipv6_get_lladdr(dev, &addr_buf))
+		if (ipv6_get_lladdr(dev, &addr_buf, 
+				   (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)))
 			return;
 		saddr = &addr_buf;
 	}
@@ -622,9 +625,33 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
 	struct sk_buff *skb;
 	struct icmp6hdr *hdr;
 	__u8 * opt;
-	int len;
+	struct inet6_ifaddr *ifp;
+	int send_sllao = dev->addr_len;
+        int len;
 	int err;
 
+
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+	/*
+	 * According to section 2.2 of RFC 4429, we must not 
+	 * send router solicitations with a sllao from 
+	 * optimistic addresses, but we may send the solicitation
+	 * if we don't include the sllao.  So here we check
+	 * if our address is optimistic, and if so, we
+	 * supress the inclusion of the sllao.
+	 */ 
+	if (send_sllao) {
+		ifp = ipv6_get_ifaddr(saddr, dev, 1);
+		if (ifp) {
+			if (ifp->flags & IFA_F_OPTIMISTIC)  {
+				send_sllao=0;
+				in6_ifa_put(ifp);
+			}
+		} else {
+			send_sllao = 0;
+		}
+	}
+#endif
 	ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr,
 			dev->ifindex);
 
@@ -637,7 +664,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
 		return;
 
 	len = sizeof(struct icmp6hdr);
-	if (dev->addr_len)
+	if (send_sllao)
 		len += ndisc_opt_addr_space(dev);
 
 	skb = sock_alloc_send_skb(sk,
@@ -664,7 +691,7 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
 
 	opt = (u8*) (hdr + 1);
 
-	if (dev->addr_len)
+	if (send_sllao)
 		ndisc_fill_addr_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr,
 				       dev->addr_len, dev->type);
 
@@ -796,28 +823,39 @@ static void ndisc_recv_ns(struct sk_buff *skb)
 	inc = ipv6_addr_is_multicast(daddr);
 
 	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {
-		if (ifp->flags & IFA_F_TENTATIVE) {
-			/* Address is tentative. If the source
-			   is unspecified address, it is someone
-			   does DAD, otherwise we ignore solicitations
-			   until DAD timer expires.
-			 */
-			if (!dad)
+
+		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
+			if (dad) {
+				if (dev->type == ARPHRD_IEEE802_TR) {
+					unsigned char *sadr = skb->mac.raw;
+					if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
+					    sadr[9] == dev->dev_addr[1] &&
+					    sadr[10] == dev->dev_addr[2] &&
+					    sadr[11] == dev->dev_addr[3] &&
+					    sadr[12] == dev->dev_addr[4] &&
+					    sadr[13] == dev->dev_addr[5]) {
+						/* looped-back to us */
+						goto out;
+					}
+				}
+
+				/*
+				 * We are colliding with another node
+				 * who is doing DAD
+				 * so fail our DAD process
+				 */
+				addrconf_dad_failure(ifp);
 				goto out;
-			if (dev->type == ARPHRD_IEEE802_TR) {
-				unsigned char *sadr = skb->mac.raw;
-				if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&
-				    sadr[9] == dev->dev_addr[1] &&
-				    sadr[10] == dev->dev_addr[2] &&
-				    sadr[11] == dev->dev_addr[3] &&
-				    sadr[12] == dev->dev_addr[4] &&
-				    sadr[13] == dev->dev_addr[5]) {
-					/* looped-back to us */
+			} else {
+				/*
+				 * This is not a dad solicitation.
+				 * If we are an optimistic node, 
+				 * we should respond.  
+				 * Otherwise, we should ignore it. 
+				 */
+				if (!(ifp->flags & IFA_F_OPTIMISTIC))
 					goto out;
-				}
 			}
-			addrconf_dad_failure(ifp);
-			return;
 		}
 
 		idev = ifp->idev;
@@ -1406,7 +1444,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
 
 	dev = skb->dev;
 
-	if (ipv6_get_lladdr(dev, &saddr_buf)) {
+	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {
 		ND_PRINTK2(KERN_WARNING
 			   "ICMPv6 Redirect: no link-local address on %s\n",
 			   dev->name);

^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-13 20:45                                                                   ` Neil Horman
@ 2007-02-13 21:46                                                                     ` YOSHIFUJI Hideaki / 吉藤英明
  2007-02-13 21:53                                                                       ` David Miller
       [not found]                                                                       ` <20070221.040259.60395625.yoshfuji@linux-ipv6.org>
  0 siblings, 2 replies; 71+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2007-02-13 21:46 UTC (permalink / raw)
  To: nhorman
  Cc: vladislav.yasevich, sri, davem, kuznet, pekkas, jmorris, kaber,
	netdev, yoshfuji

In article <20070213204515.GI25362@hmsreliant.homelinux.net> (at Tue, 13 Feb 2007 15:45:15 -0500), Neil Horman <nhorman@tuxdriver.com> says:

> Signed-off-by: Neil Horman <nhorman@tuxdriver.com>

I'm starting regression tests now.

--yoshfuji

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-13 21:46                                                                     ` YOSHIFUJI Hideaki / 吉藤英明
@ 2007-02-13 21:53                                                                       ` David Miller
       [not found]                                                                       ` <20070221.040259.60395625.yoshfuji@linux-ipv6.org>
  1 sibling, 0 replies; 71+ messages in thread
From: David Miller @ 2007-02-13 21:53 UTC (permalink / raw)
  To: yoshfuji
  Cc: nhorman, vladislav.yasevich, sri, kuznet, pekkas, jmorris, kaber, netdev

From: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date: Wed, 14 Feb 2007 06:46:49 +0900 (JST)

> In article <20070213204515.GI25362@hmsreliant.homelinux.net> (at Tue, 13 Feb 2007 15:45:15 -0500), Neil Horman <nhorman@tuxdriver.com> says:
> 
> > Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
> 
> I'm starting regression tests now.

Thank you.

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
       [not found]                                                                         ` <20070221.000222.71087924.davem@davemloft.net>
@ 2007-02-21  8:15                                                                           ` YOSHIFUJI Hideaki / 吉藤英明
  2007-02-21  9:30                                                                             ` David Miller
  0 siblings, 1 reply; 71+ messages in thread
From: YOSHIFUJI Hideaki / 吉藤英明 @ 2007-02-21  8:15 UTC (permalink / raw)
  To: davem, nhorman
  Cc: vladislav.yasevich, sri, kuznet, pekkas, jmorris, kaber, netdev,
	yoshfuji

In article <20070221.000222.71087924.davem@davemloft.net> (at Wed, 21 Feb 2007 00:02:22 -0800 (PST)), David Miller <davem@davemloft.net> says:

> > So, I think it is ready. Here's my sign-off:
> > 
> > Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
> 
> Neil, please keep this patch ready for the next merge window.

Dave, Neil,

I expect to have some changes in routing code in near future,
I want to have Neil's change in my -dev tree, if you don't mind.

--yoshfuji

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-21  8:15                                                                           ` YOSHIFUJI Hideaki / 吉藤英明
@ 2007-02-21  9:30                                                                             ` David Miller
  2007-02-21 13:37                                                                               ` Neil Horman
  0 siblings, 1 reply; 71+ messages in thread
From: David Miller @ 2007-02-21  9:30 UTC (permalink / raw)
  To: yoshfuji
  Cc: nhorman, vladislav.yasevich, sri, kuznet, pekkas, jmorris, kaber, netdev

From: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Date: Wed, 21 Feb 2007 17:15:45 +0900 (JST)

> In article <20070221.000222.71087924.davem@davemloft.net> (at Wed, 21 Feb 2007 00:02:22 -0800 (PST)), David Miller <davem@davemloft.net> says:
> 
> > > So, I think it is ready. Here's my sign-off:
> > > 
> > > Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
> > 
> > Neil, please keep this patch ready for the next merge window.
> 
> Dave, Neil,
> 
> I expect to have some changes in routing code in near future,
> I want to have Neil's change in my -dev tree, if you don't mind.

I have no problem with this, and I am sure Neil will be OK
with it too.

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection
  2007-02-21  9:30                                                                             ` David Miller
@ 2007-02-21 13:37                                                                               ` Neil Horman
  0 siblings, 0 replies; 71+ messages in thread
From: Neil Horman @ 2007-02-21 13:37 UTC (permalink / raw)
  To: David Miller
  Cc: yoshfuji, vladislav.yasevich, sri, kuznet, pekkas, jmorris,
	kaber, netdev

On Wed, Feb 21, 2007 at 01:30:31AM -0800, David Miller wrote:
> From: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
> Date: Wed, 21 Feb 2007 17:15:45 +0900 (JST)
> 
> > In article <20070221.000222.71087924.davem@davemloft.net> (at Wed, 21 Feb 2007 00:02:22 -0800 (PST)), David Miller <davem@davemloft.net> says:
> > 
> > > > So, I think it is ready. Here's my sign-off:
> > > > 
> > > > Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
> > > 
> > > Neil, please keep this patch ready for the next merge window.
> > 
> > Dave, Neil,
> > 
> > I expect to have some changes in routing code in near future,
> > I want to have Neil's change in my -dev tree, if you don't mind.
> 
> I have no problem with this, and I am sure Neil will be OK
> with it too.

Absolutely, I'll keep my devel branch rebased against Daves tree until its all
checked in, just in case any updates are needed.

Thanks for all the help everyone!
Neil


^ permalink raw reply	[flat|nested] 71+ messages in thread

end of thread, other threads:[~2007-02-21 13:42 UTC | newest]

Thread overview: 71+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-01-19 21:23 [PATCH] IPv6: Implement RFC 4429 Optimistic Duplicate Address Detection Neil Horman
2007-01-19 23:05 ` YOSHIFUJI Hideaki / 吉藤英明
2007-01-20  1:41   ` Neil Horman
2007-01-22 18:15   ` Neil Horman
2007-01-22 18:39     ` Mika Penttilä
2007-01-22 19:45       ` Neil Horman
2007-01-22 20:25         ` Vlad Yasevich
2007-01-23 18:36           ` Neil Horman
2007-01-23 19:27             ` Vlad Yasevich
2007-01-23  0:18     ` YOSHIFUJI Hideaki / 吉藤英明
2007-01-23 20:51       ` Neil Horman
2007-01-25  1:54         ` Sridhar Samudrala
2007-01-25 13:33           ` Neil Horman
2007-01-25 17:16             ` Vlad Yasevich
2007-01-25 19:45               ` Neil Horman
2007-01-25 20:18                 ` Vlad Yasevich
2007-01-25 21:26                   ` Neil Horman
2007-01-25 22:13                     ` Vlad Yasevich
2007-01-26 14:27                       ` Neil Horman
2007-01-26 15:44                         ` YOSHIFUJI Hideaki / 吉藤英明
2007-01-26 19:03                           ` Neil Horman
2007-01-25 22:34                     ` Vlad Yasevich
2007-01-26  0:13                 ` YOSHIFUJI Hideaki / 吉藤英明
2007-01-26 14:20                   ` Vlad Yasevich
2007-01-26 19:18                   ` Neil Horman
2007-01-26 20:28                     ` Vlad Yasevich
2007-01-26 21:35                       ` Neil Horman
2007-01-26 21:42                         ` Vlad Yasevich
2007-01-29 16:34                           ` Neil Horman
2007-01-29 21:30                             ` Neil Horman
2007-01-29 22:25                               ` YOSHIFUJI Hideaki / 吉藤英明
2007-01-30 13:02                                 ` Neil Horman
2007-01-30 16:16                                   ` YOSHIFUJI Hideaki / 吉藤英明
2007-01-31 20:54                                     ` Neil Horman
2007-02-02 19:06                                       ` Neil Horman
2007-02-02 19:46                                         ` David Miller
2007-02-02 20:13                                           ` Neil Horman
2007-02-02 22:22                                             ` Vlad Yasevich
2007-02-03 15:06                                               ` Neil Horman
2007-02-02 21:28                                         ` Brian Haley
2007-02-02 22:05                                           ` Vlad Yasevich
2007-02-02 23:57                                             ` Brian Haley
2007-02-03 15:05                                               ` Neil Horman
2007-02-05 17:33                                                 ` Brian Haley
2007-02-05 18:37                                                   ` Neil Horman
2007-02-02 21:50                                         ` Vlad Yasevich
2007-02-03 15:03                                           ` Neil Horman
     [not found]                                             ` <20070205205651.GB484@hmsreliant.homelinux.net>
2007-02-06  1:24                                               ` YOSHIFUJI Hideaki / 吉藤英明
2007-02-06  1:32                                                 ` David Miller
2007-02-06  1:44                                                   ` YOSHIFUJI Hideaki / 吉藤英明
2007-02-06  1:43                                                     ` David Miller
2007-02-06 12:51                                                 ` Neil Horman
2007-02-06 20:09                                                   ` Neil Horman
2007-02-06 21:13                                                     ` Vlad Yasevich
2007-02-07 20:55                                                       ` Neil Horman
2007-02-07 21:19                                                         ` Vlad Yasevich
2007-02-07 21:52                                                         ` YOSHIFUJI Hideaki / 吉藤英明
2007-02-08 13:07                                                           ` Neil Horman
2007-02-12 23:27                                                             ` YOSHIFUJI Hideaki / 吉藤英明
2007-02-13 18:22                                                               ` Neil Horman
2007-02-07 22:26                                                         ` YOSHIFUJI Hideaki / 吉藤英明
2007-02-08 16:41                                                           ` Neil Horman
2007-02-08 17:10                                                             ` YOSHIFUJI Hideaki / 吉藤英明
2007-02-08 19:32                                                               ` Neil Horman
2007-02-12 21:20                                                                 ` Neil Horman
2007-02-13 20:45                                                                   ` Neil Horman
2007-02-13 21:46                                                                     ` YOSHIFUJI Hideaki / 吉藤英明
2007-02-13 21:53                                                                       ` David Miller
     [not found]                                                                       ` <20070221.040259.60395625.yoshfuji@linux-ipv6.org>
     [not found]                                                                         ` <20070221.000222.71087924.davem@davemloft.net>
2007-02-21  8:15                                                                           ` YOSHIFUJI Hideaki / 吉藤英明
2007-02-21  9:30                                                                             ` David Miller
2007-02-21 13:37                                                                               ` Neil Horman

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.