--- linux-2.4.19/include/linux/in6.h	Tue Aug  6 20:07:24 2002
+++ linux-2.4.19-ipv6/include/linux/in6.h	Wed Aug  7 22:02:07 2002
@@ -156,6 +156,7 @@
 #define IPV6_MTU_DISCOVER	23
 #define IPV6_MTU		24
 #define IPV6_RECVERR		25
+#define IPV6_V6ONLY		26
 
 /* IPV6_MTU_DISCOVER values */
 #define IPV6_PMTUDISC_DONT		0
@@ -166,5 +167,15 @@
 #define IPV6_FLOWLABEL_MGR	32
 #define IPV6_FLOWINFO_SEND	33
 
+#define        IN6_IS_ADDR_UNSPECIFIED(a)                      \
+	((((a)->s6_addr32[0]) == 0) &&                  \
+	 (((a)->s6_addr32[1]) == 0) &&                  \
+         (((a)->s6_addr32[2]) == 0) &&                  \
+         (((a)->s6_addr32[3]) == 0))
+
+#define IN6_IS_ADDR_V4MAPPED(a)                                \
+        ((((a)->s6_addr32[0]) == 0) &&                  \
+         (((a)->s6_addr32[1]) == 0) &&                  \
+         (((a)->s6_addr32[2]) == __constant_htonl(0x0000ffff)))
 
 #endif
--- linux-2.4.19/include/linux/sysctl.h	Tue Aug  6 20:07:05 2002
+++ linux-2.4.19-ipv6/include/linux/sysctl.h	Tue Aug  6 19:52:58 2002
@@ -369,7 +369,8 @@
 	NET_IPV6_DAD_TRANSMITS=7,
 	NET_IPV6_RTR_SOLICITS=8,
 	NET_IPV6_RTR_SOLICIT_INTERVAL=9,
-	NET_IPV6_RTR_SOLICIT_DELAY=10
+	NET_IPV6_RTR_SOLICIT_DELAY=10,
+	NET_IPV6_BINDV6ONLY=11
 };
 
 /* /proc/sys/net/<protocol>/neigh/<dev> */
--- linux-2.4.19/include/net/if_inet6.h	Tue Sep 19 00:04:13 2000
+++ linux-2.4.19-ipv6/include/net/if_inet6.h	Tue Aug  6 19:48:55 2002
@@ -86,6 +86,7 @@
 	int		rtr_solicits;
 	int		rtr_solicit_interval;
 	int		rtr_solicit_delay;
+	int		bindv6only;
 
 	void		*sysctl;
 };
--- linux-2.4.19/include/net/sock.h	Tue Aug  6 20:11:22 2002
+++ linux-2.4.19-ipv6/include/net/sock.h	Wed Aug  7 22:02:24 2002
@@ -171,7 +171,8 @@
 	__u8			mc_loop:1,
 	                        recverr:1,
 	                        sndflow:1,
-	                        pmtudisc:2;
+	                        pmtudisc:2,
+				ipv6only:1;
 
 	struct ipv6_mc_socklist	*ipv6_mc_list;
 	struct ipv6_fl_socklist *ipv6_fl_list;
--- linux-2.4.19/net/ipv4/tcp_ipv4.c	Sat Aug  3 02:39:46 2002
+++ linux-2.4.19-ipv6/net/ipv4/tcp_ipv4.c	Wed Aug  7 22:05:16 2002
@@ -177,23 +177,100 @@
 static inline int tcp_bind_conflict(struct sock *sk, struct tcp_bind_bucket *tb)
 {
 	struct sock *sk2 = tb->owners;
-	int sk_reuse = sk->reuse;
+        int sk_reuse, sk2_reuse;
+        int addr_type2;
 	
-	for( ; sk2 != NULL; sk2 = sk2->bind_next) {
-		if (sk != sk2 &&
-		    sk2->reuse <= 1 &&
-		    sk->bound_dev_if == sk2->bound_dev_if) {
-			if (!sk_reuse	||
-			    !sk2->reuse	||
-			    sk2->state == TCP_LISTEN) {
-				if (!sk2->rcv_saddr	||
-				    !sk->rcv_saddr	||
-				    (sk2->rcv_saddr == sk->rcv_saddr))
-					break;
-			}
-		}
-	}
-	return sk2 != NULL;
+        int ret;
+
+        sk_reuse = 0;
+        if (sk->reuse)
+                sk_reuse |= MULTICAST(sk->rcv_saddr) ? 3 : 1;
+
+        for( ; sk2 != NULL; sk2 = sk2->bind_next) {
+                int both_specified = 0;
+
+                if (sk2 == sk ||
+                    (sk2->bound_dev_if && sk->bound_dev_if &&
+                     sk2->bound_dev_if != sk->bound_dev_if))
+                        continue;
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+                if (sk2->family == AF_INET6) {
+                        struct in6_addr *sk2_rcv_saddr6 = sk2->state != TCP_TIME_WAIT ?
+                                                                &sk2->net_pinfo.af_inet6.rcv_saddr :
+                                                                &((struct tcp_tw_bucket*)sk2)->v6_rcv_saddr;
+                        if (IN6_IS_ADDR_UNSPECIFIED(sk2_rcv_saddr6))
+                                addr_type2 = IPV6_ADDR_ANY;
+                        else if (IN6_IS_ADDR_V4MAPPED(sk2_rcv_saddr6))
+                                addr_type2 = IPV6_ADDR_MAPPED;
+                        else
+                                addr_type2 = IPV6_ADDR_UNICAST; /*XXX*/
+                } else
+                        addr_type2 = IPV6_ADDR_MAPPED;
+#else
+                addr_type2 = IPV6_ADDR_MAPPED;
+#endif
+                if ((addr_type2 != IPV6_ADDR_MAPPED ? addr_type2 != IPV6_ADDR_ANY : sk2->rcv_saddr) &&
+                    sk->rcv_saddr) {
+                        if (sk2->rcv_saddr != sk->rcv_saddr)
+                                continue;
+                        both_specified = 1;
+                }
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+                if (addr_type2 != IPV6_ADDR_MAPPED && sk2->net_pinfo.af_inet6.ipv6only) {
+                        continue;
+                }
+#endif
+
+                sk2_reuse = 0;
+                if (sk2->reuse)
+                        sk2_reuse |= addr_type2 != IPV6_ADDR_MAPPED ?
+                                        ((addr_type2 & IPV6_ADDR_MULTICAST) ? 3 
+: 1) :
+                                        (MULTICAST(sk2->rcv_saddr) ? 3 : 1);
+
+                if (sk2_reuse & sk_reuse) {     /* NOT && */
+                        ret = 1;
+                        if (sk2_reuse & sk_reuse & 2) {
+                                continue;
+                        } else {
+                                if (both_specified) {
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+                                        struct in6_addr *sk2_daddr6 = sk2->state != TCP_TIME_WAIT ?
+                                                                        &sk2->net_pinfo.af_inet6.daddr :
+                                                                        &((struct tcp_tw_bucket*)sk2)->v6_daddr;
+#endif
+                                        int addr_type2d;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+                                        if (sk2->family == AF_INET6) {
+                                                if (IN6_IS_ADDR_UNSPECIFIED(sk2_daddr6))
+                                                        addr_type2d = IPV6_ADDR_ANY;
+                                                else if (IN6_IS_ADDR_V4MAPPED(sk2_daddr6))
+                                                        addr_type2d = IPV6_ADDR_MAPPED;
+                                                else
+                                                        addr_type2d = IPV6_ADDR_UNICAST; /*XXX*/
+                                        } else
+                                                addr_type2d = IPV6_ADDR_MAPPED;
+#else
+                                        addr_type2d = IPV6_ADDR_MAPPED;
+#endif
+                                        if (addr_type2d != IPV6_ADDR_MAPPED ? addr_type2d != IPV6_ADDR_ANY : sk2->daddr)
+                                                continue;
+                                } else {
+                                        if ((addr_type2 != IPV6_ADDR_MAPPED ? addr_type2 != IPV6_ADDR_ANY : sk2->rcv_saddr) ||
+                                            sk->rcv_saddr)
+                                                continue;
+                                }
+                        }
+                }
+                ret = 1;
+                goto failed;
+        }
+        /* If we found a conflict, fail. */
+        ret = sk2 != NULL;
+failed:
+        return ret;
 }
 
 /* Obtain a reference to a local port for the given sock,
@@ -247,29 +324,14 @@
 				break;
 	}
 	if (tb != NULL && tb->owners != NULL) {
-		if (sk->reuse > 1)
-			goto success;
-		if (tb->fastreuse > 0 && sk->reuse != 0 && sk->state != TCP_LISTEN) {
-			goto success;
-		} else {
-			ret = 1;
-			if (tcp_bind_conflict(sk, tb))
-				goto fail_unlock;
-		}
+                ret = 1; 
+                if (tcp_bind_conflict(sk, tb))
+                        goto fail_unlock; 
 	}
 	ret = 1;
 	if (tb == NULL &&
 	    (tb = tcp_bucket_create(head, snum)) == NULL)
 			goto fail_unlock;
-	if (tb->owners == NULL) {
-		if (sk->reuse && sk->state != TCP_LISTEN)
-			tb->fastreuse = 1;
-		else
-			tb->fastreuse = 0;
-	} else if (tb->fastreuse &&
-		   ((sk->reuse == 0) || (sk->state == TCP_LISTEN)))
-		tb->fastreuse = 0;
-success:
 	if (sk->prev == NULL)
 		tcp_bind_hash(sk, tb, snum);
 	BUG_TRAP(sk->prev == (struct sock *) tb);
@@ -417,24 +479,35 @@
 {
 	struct sock *result = NULL;
 	int score, hiscore;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+        static const int maxscore = 5;
+#else
+        static const int maxscore = 4;
+#endif
 
-	hiscore=0;
+	hiscore=-1;
 	for(; sk; sk = sk->next) {
 		if(sk->num == hnum) {
 			__u32 rcv_saddr = sk->rcv_saddr;
 
-			score = 1;
+                        score = 0;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+                        if (sk->family == PF_INET)
+                                score++;
+                        else if (sk->net_pinfo.af_inet6.ipv6only)
+                                continue;
+#endif
 			if(rcv_saddr) {
 				if (rcv_saddr != daddr)
 					continue;
-				score++;
+				score=+2;
 			}
 			if (sk->bound_dev_if) {
 				if (sk->bound_dev_if != dif)
 					continue;
-				score++;
+				score=+2;
 			}
-			if (score == 3)
+			if (score == maxscore)
 				return sk;
 			if (score > hiscore) {
 				hiscore = score;
--- linux-2.4.19/net/ipv4/udp.c	Sat Aug  3 02:39:46 2002
+++ linux-2.4.19-ipv6/net/ipv4/udp.c	Wed Aug  7 22:07:24 2002
@@ -85,6 +85,7 @@
 #include <linux/netdevice.h>
 #include <net/snmp.h>
 #include <net/ip.h>
+#include <net/ipv6.h>
 #include <net/protocol.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
@@ -153,18 +154,94 @@
 		udp_port_rover = snum = result;
 	} else {
 		struct sock *sk2;
+                int sk_reuse, sk2_reuse;
+                int addr_type2;
+
+                sk_reuse = 0;
+                if (sk->reuse)
+                        sk_reuse |= MULTICAST(sk->rcv_saddr) ? 3 : 1;
 
 		for (sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)];
 		     sk2 != NULL;
 		     sk2 = sk2->next) {
-			if (sk2->num == snum &&
-			    sk2 != sk &&
-			    sk2->bound_dev_if == sk->bound_dev_if &&
-			    (!sk2->rcv_saddr ||
-			     !sk->rcv_saddr ||
-			     sk2->rcv_saddr == sk->rcv_saddr) &&
-			    (!sk2->reuse || !sk->reuse))
-				goto fail;
+                        int both_specified = 0;
+
+                        if (sk2->num != snum ||
+                            sk2 == sk ||
+                            (sk2->bound_dev_if && sk->bound_dev_if &&
+                             sk2->bound_dev_if != sk->bound_dev_if))
+                                continue;
+#if 0
+                        if (sk2->family != AF_INET6 && sk2->family != AF_INET)
+                                continue;
+#endif
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+                        if (sk2->family == AF_INET6) {
+                                if (IN6_IS_ADDR_UNSPECIFIED(&sk2->net_pinfo.af_inet6.rcv_saddr))
+                                        addr_type2 = IPV6_ADDR_ANY;
+                                else if (IN6_IS_ADDR_V4MAPPED(&sk2->net_pinfo.af_inet6.rcv_saddr))
+                                        addr_type2 = IPV6_ADDR_MAPPED;
+                                else
+                                        addr_type2 = IPV6_ADDR_UNICAST; /*XXX*/
+                        } else
+                                addr_type2 = IPV6_ADDR_MAPPED;
+#else
+                        addr_type2 = IPV6_ADDR_MAPPED;
+#endif
+
+                        if ((addr_type2 != IPV6_ADDR_MAPPED ? addr_type2 != IPV6_ADDR_ANY : sk2->rcv_saddr) &&
+                            sk->rcv_saddr) {
+                                if (sk2->rcv_saddr != sk->rcv_saddr)
+                                        continue;
+                                both_specified = 1;
+                        }
+
+#if defined(CONFIG_NET_MODERATE_REUSE) || defined(CONFIG_IPV6_MODERATE_DOUBLE_BIND)
+                        uid_ok = sk2_uid == (uid_t) -1 || sk_uid == sk2_uid;
+#endif
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+                        if (addr_type2 != IPV6_ADDR_MAPPED && sk2->net_pinfo.af_inet6.ipv6only) {
+                                continue;
+                        }
+#endif
+
+                        sk2_reuse = 0;
+                        if (sk2->reuse)
+                                sk2_reuse |= addr_type2 != IPV6_ADDR_MAPPED ?
+                                                ((addr_type2 & IPV6_ADDR_MULTICAST) ? 3 : 1) :
+                                                (MULTICAST(sk2->rcv_saddr) ? 3 : 1);
+
+                        if (sk2_reuse & sk_reuse) {     /* NOT && */
+                                if (sk2_reuse & sk_reuse & 2) {
+                                        continue;
+                                } else {
+                                        if (both_specified) {
+                                                int addr_type2d;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+                                                if (sk2->family == AF_INET6) {
+                                                        if (IN6_IS_ADDR_UNSPECIFIED(&sk2->net_pinfo.af_inet6.daddr))
+                                                                addr_type2d = IPV6_ADDR_ANY;
+                                                        else if (IN6_IS_ADDR_V4MAPPED(&sk2->net_pinfo.af_inet6.daddr))
+                                                                addr_type2d = IPV6_ADDR_MAPPED;
+                                                        else
+                                                                addr_type2d = IPV6_ADDR_UNICAST; /*XXX*/
+                                                } else
+                                                        addr_type2d = IPV6_ADDR_MAPPED;
+#else
+                                                addr_type2d = IPV6_ADDR_MAPPED;
+#endif
+                                                if (addr_type2d != IPV6_ADDR_MAPPED ? addr_type2d != IPV6_ADDR_ANY : sk2->daddr)
+                                                        continue;
+                                        } else {
+                                                if ((addr_type2 != IPV6_ADDR_MAPPED ? addr_type2 != IPV6_ADDR_ANY : sk2->rcv_saddr) ||
+                                                    sk->rcv_saddr)
+                                                        continue;
+                                        }
+                                }
+                        }
+                        goto fail;
 		}
 	}
 	sk->num = snum;
@@ -213,31 +290,42 @@
 	struct sock *sk, *result = NULL;
 	unsigned short hnum = ntohs(dport);
 	int badness = -1;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+        static const int maxscore = 9;
+#else
+        static const int maxscore = 8;
+#endif
 
 	for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
 		if(sk->num == hnum) {
 			int score = 0;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+                        if(sk->family == PF_INET)
+                                score++;
+                        else if (sk->net_pinfo.af_inet6.ipv6only)
+                                continue;
+#endif
 			if(sk->rcv_saddr) {
 				if(sk->rcv_saddr != daddr)
 					continue;
-				score++;
+				score=+2;
 			}
 			if(sk->daddr) {
 				if(sk->daddr != saddr)
 					continue;
-				score++;
+				score=+2;
 			}
 			if(sk->dport) {
 				if(sk->dport != sport)
 					continue;
-				score++;
+				score=+2;
 			}
 			if(sk->bound_dev_if) {
 				if(sk->bound_dev_if != dif)
 					continue;
-				score++;
+				score=+2;
 			}
-			if(score == 4) {
+			if(score == maxscore) {
 				result = sk;
 				break;
 			} else if(score > badness) {
--- linux-2.4.19/net/ipv6/addrconf.c	Sat Aug  3 02:39:46 2002
+++ linux-2.4.19-ipv6/net/ipv6/addrconf.c	Wed Aug  7 20:07:41 2002
@@ -116,6 +116,7 @@
 	MAX_RTR_SOLICITATIONS,		/* router solicits	*/
 	RTR_SOLICITATION_INTERVAL,	/* rtr solicit interval	*/
 	MAX_RTR_SOLICITATION_DELAY,	/* rtr solicit delay	*/
+	0,				/* bindv6only           */
 };
 
 static struct ipv6_devconf ipv6_devconf_dflt =
@@ -130,6 +131,7 @@
 	MAX_RTR_SOLICITATIONS,		/* router solicits	*/
 	RTR_SOLICITATION_INTERVAL,	/* rtr solicit interval	*/
 	MAX_RTR_SOLICITATION_DELAY,	/* rtr solicit delay	*/
+	0,				/* bindv6only           */
 };
 
 int ipv6_addr_type(struct in6_addr *addr)
@@ -1879,7 +1881,7 @@
 static struct addrconf_sysctl_table
 {
 	struct ctl_table_header *sysctl_header;
-	ctl_table addrconf_vars[11];
+	ctl_table addrconf_vars[13];
 	ctl_table addrconf_dev[2];
 	ctl_table addrconf_conf_dir[2];
 	ctl_table addrconf_proto_dir[2];
@@ -1926,6 +1928,10 @@
          &ipv6_devconf.rtr_solicit_delay, sizeof(int), 0644, NULL,
          &proc_dointvec_jiffies},
 
+        {NET_IPV6_BINDV6ONLY, "bindv6only",
+         &ipv6_devconf.bindv6only, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+
 	{0}},
 
 	{{NET_PROTO_CONF_ALL, "all", NULL, 0, 0555, addrconf_sysctl.addrconf_vars},{0}},
--- linux-2.4.19/net/ipv6/af_inet6.c	Wed Oct 17 23:16:39 2001
+++ linux-2.4.19-ipv6/net/ipv6/af_inet6.c	Wed Aug  7 20:39:56 2002
@@ -172,6 +172,7 @@
 	sk->net_pinfo.af_inet6.mcast_hops = -1;
 	sk->net_pinfo.af_inet6.mc_loop	  = 1;
 	sk->net_pinfo.af_inet6.pmtudisc	  = IPV6_PMTUDISC_WANT;
+	sk->net_pinfo.af_inet6.ipv6only   = ipv6_devconf.bindv6only;
 
 	/* Init the ipv4 part of the socket since we can have sockets
 	 * using v6 API for ipv4.
@@ -292,6 +293,11 @@
 			return -EINVAL;
 		}
 	}
+        else if ((addr_type == IPV6_ADDR_MAPPED) && sk->net_pinfo.af_inet6.ipv6only) {
+                release_sock(sk);
+                return -EINVAL;
+        }
+
 
 	sk->rcv_saddr = v4addr;
 	sk->saddr = v4addr;
--- linux-2.4.19/net/ipv6/ipv6_sockglue.c	Thu Sep 20 23:12:56 2001
+++ linux-2.4.19-ipv6/net/ipv6/ipv6_sockglue.c	Wed Aug  7 00:13:20 2002
@@ -380,6 +380,15 @@
 		retv = ipv6_flowlabel_opt(sk, optval, optlen);
 		break;
 
+        case IPV6_V6ONLY:
+        	if (optlen != sizeof(int))
+			goto e_inval;
+                if (sk->userlocks&SOCK_BINDADDR_LOCK)
+                        goto e_inval;
+                np->ipv6only = valbool;
+                retv = 0;
+                break;
+
 #ifdef CONFIG_NETFILTER
 	default:
 		retv = nf_setsockopt(sk, PF_INET6, optname, optval, 
@@ -522,6 +531,10 @@
 		val = np->sndflow;
 		break;
 
+	case IPV6_V6ONLY:
+                val = np->ipv6only;
+                break;
+
 	default:
 #ifdef CONFIG_NETFILTER
 		lock_sock(sk);
--- linux-2.4.19/net/ipv6/tcp_ipv6.c	Sat Aug  3 02:39:46 2002
+++ linux-2.4.19-ipv6/net/ipv6/tcp_ipv6.c	Wed Aug  7 22:26:07 2002
@@ -133,32 +133,90 @@
 				break;
 	}
 	if (tb != NULL && tb->owners != NULL) {
-		if (tb->fastreuse > 0 && sk->reuse != 0 && sk->state != TCP_LISTEN) {
-			goto success;
-		} else {
+		{
 			struct sock *sk2 = tb->owners;
-			int sk_reuse = sk->reuse;
-			int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
+			int sk_reuse, sk2_reuse;
+			struct in6_addr *sk_rcv_saddr6 = sk->state != TCP_TIME_WAIT ? 
+								&sk->net_pinfo.af_inet6.rcv_saddr:
+								&((struct tcp_tw_bucket*)sk2)->v6_rcv_saddr;
+			int addr_type = ipv6_addr_type(sk_rcv_saddr6),
+			    addr_type2;
+
+			sk_reuse = 0;
+			if (sk->reuse)
+				sk_reuse |= addr_type != IPV6_ADDR_MAPPED ?
+						((addr_type & IPV6_ADDR_MULTICAST) ? 3 : 1) :
+						(MULTICAST(sk->rcv_saddr) ? 3 : 1);
 
 			/* We must walk the whole port owner list in this case. -DaveM */
 			for( ; sk2 != NULL; sk2 = sk2->bind_next) {
-				if (sk != sk2 &&
-				    sk->bound_dev_if == sk2->bound_dev_if) {
-					if (!sk_reuse	||
-					    !sk2->reuse	||
-					    sk2->state == TCP_LISTEN) {
-						/* NOTE: IPv6 tw bucket have different format */
-						if (!sk2->rcv_saddr	||
-						    addr_type == IPV6_ADDR_ANY ||
-						    !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
-								   sk2->state != TCP_TIME_WAIT ?
-								   &sk2->net_pinfo.af_inet6.rcv_saddr :
-								   &((struct tcp_tw_bucket*)sk)->v6_rcv_saddr) ||
-						    (addr_type==IPV6_ADDR_MAPPED && sk2->family==AF_INET &&
-						     sk->rcv_saddr==sk2->rcv_saddr))
-							break;
+				int both_specified = 0;
+				struct in6_addr *sk2_rcv_saddr6;
+				if (sk2 == sk ||
+				    (sk2->bound_dev_if && sk->bound_dev_if &&
+				     sk2->bound_dev_if != sk->bound_dev_if))
+					continue;
+#if 0
+				if (sk2->family != AF_INET6 && sk2->family != AF_INET)
+					continue;
+#endif
+				sk2_rcv_saddr6 = sk2->state != TCP_TIME_WAIT ?
+							&sk2->net_pinfo.af_inet6.rcv_saddr :
+							&((struct tcp_tw_bucket*)sk2)->v6_rcv_saddr;
+				addr_type2 = sk2->family == AF_INET6 ? ipv6_addr_type(sk2_rcv_saddr6) : IPV6_ADDR_MAPPED;
+
+				if ((addr_type2 != IPV6_ADDR_MAPPED ? addr_type2 != IPV6_ADDR_ANY : sk2->rcv_saddr) &&
+				    (addr_type != IPV6_ADDR_MAPPED ? addr_type != IPV6_ADDR_ANY : sk->rcv_saddr)) {
+					if (addr_type2 == IPV6_ADDR_MAPPED || addr_type == IPV6_ADDR_MAPPED) {
+						if (addr_type2 != addr_type ||
+						    sk2->rcv_saddr != sk->rcv_saddr)
+							continue;
+					} else {
+						if (ipv6_addr_cmp(sk2_rcv_saddr6, sk_rcv_saddr6))
+							continue;
 					}
+					both_specified = 1;
+				}
+
+				if ((addr_type2 == IPV6_ADDR_MAPPED && 
+				     addr_type != IPV6_ADDR_MAPPED && sk->net_pinfo.af_inet6.ipv6only) ||
+				    (addr_type == IPV6_ADDR_MAPPED &&
+				     addr_type2 != IPV6_ADDR_MAPPED && sk2->net_pinfo.af_inet6.ipv6only)) {
+#ifdef CONFIG_IPV6_MODERATE_DOUBLE_BIND
+					if (uid_ok)
+						continue;
+#else
+					continue;
+#endif
 				}
+
+				sk2_reuse = 0;
+				if (sk2->reuse)
+					sk2_reuse |= addr_type2 != IPV6_ADDR_MAPPED ?
+							((addr_type2 & IPV6_ADDR_MULTICAST) ? 3 : 1) :
+							(MULTICAST(sk2->rcv_saddr) ? 3 : 1);
+
+				if (sk2_reuse & sk_reuse) {	/* NOT && */
+					ret = 1;
+					if (sk2_reuse & sk_reuse & 2) {
+						continue;
+					} else {
+						if (both_specified) {
+							struct in6_addr *sk2_daddr6 = sk2->state != TCP_TIME_WAIT ?
+											&sk2->net_pinfo.af_inet6.daddr :
+											&((struct tcp_tw_bucket*)sk2)->v6_daddr;
+							int addr_type2d = sk2->family == AF_INET6 ? ipv6_addr_type(sk2_daddr6) : IPV6_ADDR_MAPPED;
+							if (addr_type2d != IPV6_ADDR_MAPPED ? addr_type2d != IPV6_ADDR_ANY : sk2->daddr)
+								continue;
+						} else {
+							if ((addr_type2 != IPV6_ADDR_MAPPED ? addr_type2 != IPV6_ADDR_ANY : sk2->rcv_saddr) || 
+							    (addr_type != IPV6_ADDR_MAPPED ? addr_type != IPV6_ADDR_ANY : sk->rcv_saddr))
+								continue;
+						}
+					}
+				}
+				ret = 1;
+				goto fail_unlock;
 			}
 			/* If we found a conflict, fail. */
 			ret = 1;
@@ -179,7 +237,6 @@
 		   ((sk->reuse == 0) || (sk->state == TCP_LISTEN)))
 		tb->fastreuse = 0;
 
-success:
 	sk->num = snum;
 	if (sk->prev == NULL) {
 		if ((sk->bind_next = tb->owners) != NULL)
@@ -602,6 +659,12 @@
 
 		SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
 
+		if (sk->net_pinfo.af_inet6.ipv6only)
+			return -ENETUNREACH;
+
+		if (sk->net_pinfo.af_inet6.ipv6only)
+			return -ENETUNREACH;
+
 		sin.sin_family = AF_INET;
 		sin.sin_port = usin->sin6_port;
 		sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
--- linux-2.4.19/net/ipv6/udp.c	Sat Aug  3 02:39:46 2002
+++ linux-2.4.19-ipv6/net/ipv6/udp.c	Wed Aug  7 22:27:02 2002
@@ -98,23 +98,76 @@
 		udp_port_rover = snum = result;
 	} else {
 		struct sock *sk2;
-		int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
+		int sk_reuse, sk2_reuse;
+		int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr),
+		    addr_type2;
+
+		sk_reuse = 0;
+		if (sk->reuse)
+			sk_reuse |= addr_type != IPV6_ADDR_MAPPED ?
+					((addr_type & IPV6_ADDR_MULTICAST) ? 3 : 1) :
+					(MULTICAST(sk->rcv_saddr) ? 3 : 1);
 
 		for (sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)];
 		     sk2 != NULL;
 		     sk2 = sk2->next) {
-			if (sk2->num == snum &&
-			    sk2 != sk &&
-			    sk2->bound_dev_if == sk->bound_dev_if &&
-			    (!sk2->rcv_saddr ||
-			     addr_type == IPV6_ADDR_ANY ||
-			     !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
-					    &sk2->net_pinfo.af_inet6.rcv_saddr) ||
-			     (addr_type == IPV6_ADDR_MAPPED &&
-			      sk2->family == AF_INET &&
-			      sk->rcv_saddr == sk2->rcv_saddr)) &&
-			    (!sk2->reuse || !sk->reuse))
-				goto fail;
+			int both_specified = 0;
+
+			if (sk2->num != snum ||
+			    sk2 == sk ||
+			    (sk2->bound_dev_if && sk->bound_dev_if &&
+			     sk2->bound_dev_if != sk->bound_dev_if))
+				continue;
+#if 0
+			if (sk2->family != AF_INET6 && sk2->family != AF_INET)
+				continue;
+#endif
+
+			addr_type2 = sk2->family == AF_INET6 ? ipv6_addr_type(&sk2->net_pinfo.af_inet6.rcv_saddr) : IPV6_ADDR_MAPPED;
+
+			if ((addr_type2 != IPV6_ADDR_MAPPED ? addr_type2 != IPV6_ADDR_ANY : sk2->rcv_saddr) &&
+			    (addr_type != IPV6_ADDR_MAPPED ? addr_type != IPV6_ADDR_ANY : sk->rcv_saddr)) {
+				if (addr_type2 == IPV6_ADDR_MAPPED || addr_type == IPV6_ADDR_MAPPED) {
+					if (addr_type2 != addr_type ||
+					    sk2->rcv_saddr != sk->rcv_saddr)
+						continue;
+				} else {
+					if (ipv6_addr_cmp(&sk2->net_pinfo.af_inet6.rcv_saddr,
+							  &sk->net_pinfo.af_inet6.rcv_saddr))
+						continue;
+				}
+				both_specified = 1;
+			}
+
+			if ((addr_type2 == IPV6_ADDR_MAPPED && 
+			     addr_type != IPV6_ADDR_MAPPED && sk->net_pinfo.af_inet6.ipv6only) ||
+			    (addr_type == IPV6_ADDR_MAPPED &&
+			     addr_type2 != IPV6_ADDR_MAPPED && sk2->net_pinfo.af_inet6.ipv6only)) {
+				continue;
+			}
+
+			sk2_reuse = 0;
+			if (sk2->reuse)
+				sk2_reuse |= addr_type2 != IPV6_ADDR_MAPPED ?
+						((addr_type2 & IPV6_ADDR_MULTICAST) ? 3 : 1) :
+						(MULTICAST(sk2->rcv_saddr) ? 3 : 1);
+
+			if (sk2_reuse & sk_reuse) {	/* NOT && */
+				if (sk2_reuse & sk_reuse & 2) {
+					continue;
+				} else {
+					if (both_specified) {
+						int addr_type2d = sk2->family == AF_INET6 ? ipv6_addr_type(&sk2->net_pinfo.af_inet6.daddr) : IPV6_ADDR_MAPPED;
+						if (addr_type2d != IPV6_ADDR_MAPPED ? addr_type2d != IPV6_ADDR_ANY : sk2->daddr)
+							continue;
+					} else {
+						if ((addr_type2 != IPV6_ADDR_MAPPED ? addr_type2 != IPV6_ADDR_ANY : sk2->rcv_saddr) || 
+						    (addr_type != IPV6_ADDR_MAPPED ? addr_type != IPV6_ADDR_ANY : sk->rcv_saddr))
+							continue;
+					}
+				}
+			}
+			goto fail;
 		}
 	}
 
@@ -784,7 +837,8 @@
 
 	if (sin6) {
 		if (sin6->sin6_family == AF_INET)
-			return udp_sendmsg(sk, msg, ulen);
+			return !sk->net_pinfo.af_inet6.ipv6only ?
+				udp_sendmsg(sk, msg, ulen) : -ENETUNREACH;
 
 		if (addr_len < SIN6_LEN_RFC2133)
 			return -EINVAL;
@@ -831,6 +885,9 @@
 	if (addr_type == IPV6_ADDR_MAPPED) {
 		struct sockaddr_in sin;
 
+		if (sk->net_pinfo.af_inet6.ipv6only)
+			return -ENETUNREACH;
+
 		sin.sin_family = AF_INET;
 		sin.sin_addr.s_addr = daddr->s6_addr32[3];
 		sin.sin_port = udh.uh.dest;