Thread (33 messages) 33 messages, 7 authors, 2002-10-25

Re: [PATCH] IPv6: Allow Both IPv6 and IPv4 Sockets on the Same Port

From: <hidden>
Date: 2002-10-03 13:01:11

Hello!
We also prohibit a completely duplicate set of (local-addr, local-port,
remote-addr, remote-port) set even if SO_REUSEADDR is set unless
the local address is a multicast address; it is ambiguous and it may 
steal packets from others; i.e. a kind of DoS.
This part of the patch is noop.

While doing *_get_port() daddr/dport are _unknown_ and always zero,
so it never works.

Please, remove these bits, the patch will become simpler.

What's about the problem, it cannot be a problem for TCP, connection
uniqueness is verified by tcp_*_check_established() not depending
on value of SO_REUSEADDR. What's about UDP, the problem really might
be a real problem, let's defer the issue, it looks absoluteky unrelated.


BTW the question: why is bindv6only in device configuration directory?

Alexey



quoted hunk ↗ jump to hunk
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux24/include/net/if_inet6.h,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.8.1
diff -u -r1.1.1.1 -r1.1.1.1.8.1
--- include/net/if_inet6.h	2002/08/20 09:46:45	1.1.1.1
+++ include/net/if_inet6.h	2002/09/11 03:30:27	1.1.1.1.8.1
@@ -86,6 +86,7 @@
 	int		rtr_solicits;
 	int		rtr_solicit_interval;
 	int		rtr_solicit_delay;
+	int		bindv6only;
 
 	void		*sysctl;
 };
Index: include/net/sock.h
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux24/include/net/sock.h,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.8.1
diff -u -r1.1.1.1 -r1.1.1.1.8.1
--- include/net/sock.h	2002/08/20 09:46:45	1.1.1.1
+++ include/net/sock.h	2002/09/11 03:30:27	1.1.1.1.8.1
@@ -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;
Index: net/ipv4/tcp_ipv4.c
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux24/net/ipv4/tcp_ipv4.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.8.1
diff -u -r1.1.1.1 -r1.1.1.1.8.1
--- net/ipv4/tcp_ipv4.c	2002/08/20 09:47:02	1.1.1.1
+++ net/ipv4/tcp_ipv4.c	2002/09/11 03:30:27	1.1.1.1.8.1
@@ -45,6 +45,14 @@
  *	Vitaly E. Lavrov	:	Transparent proxy revived after year coma.
  *	Andi Kleen		:	Fix new listen.
  *	Andi Kleen		:	Fix accept error reporting.
+ *	YOSHIFUJI Hideaki @USAGI:	Reworked bind(2) behavior including:
+ *					- Allow ipv6 and ipv4 bind(2) to the
+ *					  same port if IPV6_V6ONLY socket option
+ *					  is set.
+ *					- Don't allow binding to the same
+ *					  address unless it is one of multi-
+ *					  cast address even if SO_REUSEADDR 
+ *					  is set.
  */
 
 #include <linux/config.h>
@@ -177,23 +185,92 @@
 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;
+	int ret;
+
+	sk_reuse = 0;
+	if (sk->reuse)
+		sk_reuse |= 1;
 	
 	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;
+		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 |= 1;
+
+		if (sk2_reuse & sk_reuse & 3) {	/* NOT && */
+			ret = 1;
+			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;
 	}
-	return sk2 != NULL;
+	/* If we found a conflict, fail. */
+	ret = sk2 != NULL;
+failed:
+	return ret;
 }
 
 /* Obtain a reference to a local port for the given sock,
@@ -247,10 +324,11 @@
 				break;
 	}
 	if (tb != NULL && tb->owners != NULL) {
-		if (sk->reuse > 1)
+		ret = 1; 
+		if (tb->fastreuse > 0 && 
+		    sk->reuse != 0 &&
+		    sk->state != TCP_LISTEN) {
 			goto success;
-		if (tb->fastreuse > 0 && sk->reuse != 0 && sk->state != TCP_LISTEN) {
-			goto success;
 		} else {
 			ret = 1;
 			if (tcp_bind_conflict(sk, tb))
@@ -418,23 +496,31 @@
 	struct sock *result = NULL;
 	int score, hiscore;
 
-	hiscore=0;
+	hiscore = -1;
 	for(; sk; sk = sk->next) {
 		if(sk->num == hnum) {
 			__u32 rcv_saddr = sk->rcv_saddr;
 
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+			score = 0;
+			if (sk->family == PF_INET)
+				score++;
+			else if (sk->net_pinfo.af_inet6.ipv6only)
+				continue;
+#else
 			score = 1;
+#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 == 5)
 				return sk;
 			if (score > hiscore) {
 				hiscore = score;
@@ -456,6 +542,10 @@
 		if (sk->num == hnum &&
 		    sk->next == NULL &&
 		    (!sk->rcv_saddr || sk->rcv_saddr == daddr) &&
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		    (sk->family == PF_INET ||
+		     (sk->family == PF_INET6 && !sk->net_pinfo.af_inet6.ipv6only)) &&
+#endif
 		    !sk->bound_dev_if)
 			goto sherry_cache;
 		sk = __tcp_v4_lookup_listener(sk, daddr, hnum, dif);
Index: net/ipv4/udp.c
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux24/net/ipv4/udp.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.8.1
diff -u -r1.1.1.1 -r1.1.1.1.8.1
--- net/ipv4/udp.c	2002/08/20 09:47:02	1.1.1.1
+++ net/ipv4/udp.c	2002/09/11 03:30:27	1.1.1.1.8.1
@@ -61,6 +61,14 @@
  *					return ENOTCONN for unconnected sockets (POSIX)
  *		Janos Farkas	:	don't deliver multi/broadcasts to a different
  *					bound-to-device socket
+ *	YOSHIFUJI Hideaki @USAGI:	Reworked bind(2) behavior, including:
+ *					- Allow ipv6 and ipv4 bind(2) to the
+ *					  same port if IPV6_V6ONLY socket opttion is
+ *					  is set.
+ *					- Don't allow binding to the same
+ *					  address unless it is one of multi-
+ *					  cast address even if SO_REUSEADDR 
+ *					  is set.
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -85,6 +93,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 +162,88 @@
 		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 |= 1;
+		if (sk_reuse &&
+		    MULTICAST(sk->rcv_saddr))
+			sk_reuse |= 4;
+
 		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 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_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 |= 1;
+			if (sk2_reuse &&
+			    (addr_type2 != IPV6_ADDR_MAPPED ? (addr_type2 & IPV6_ADDR_MULTICAST) : MULTICAST(sk2->rcv_saddr)))
+				sk2_reuse |= 4;
+
+			if (sk2_reuse & sk_reuse & 3) {	/* NOT && */
+				if (sk2_reuse & sk_reuse & 4)
+					continue;
+				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;
@@ -216,28 +295,37 @@
 
 	for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
 		if(sk->num == hnum) {
-			int score = 0;
+			int score;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+			score = 0;
+			if(sk->family == PF_INET)
+				score++;
+			else if (sk->net_pinfo.af_inet6.ipv6only)
+				continue;
+#else
+			score = 1;
+#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 == 9) {
 				result = sk;
 				break;
 			} else if(score > badness) {
@@ -273,6 +361,9 @@
 		    (s->daddr && s->daddr!=rmt_addr)			||
 		    (s->dport != rmt_port && s->dport != 0)			||
 		    (s->rcv_saddr  && s->rcv_saddr != loc_addr)		||
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		    (s->family != PF_INET && s->net_pinfo.af_inet6.ipv6only)	||
+#endif
 		    (s->bound_dev_if && s->bound_dev_if != dif))
 			continue;
 		break;
Index: net/ipv6/addrconf.c
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux24/net/ipv6/addrconf.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.8.1
diff -u -r1.1.1.1 -r1.1.1.1.8.1
--- net/ipv6/addrconf.c	2002/08/20 09:47:02	1.1.1.1
+++ net/ipv6/addrconf.c	2002/09/11 03:30:27	1.1.1.1.8.1
@@ -116,6 +116,7 @@
 	MAX_RTR_SOLICITATIONS,		/* router solicits	*/
 	RTR_SOLICITATION_INTERVAL,	/* rtr solicit interval	*/
 	MAX_RTR_SOLICITATION_DELAY,	/* rtr solicit delay	*/
+	bindv6only:		0,
 };
 
 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	*/
+	bindv6only:		0,
 };
 
 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[12];
 	ctl_table addrconf_dev[2];
 	ctl_table addrconf_conf_dir[2];
 	ctl_table addrconf_proto_dir[2];
@@ -1925,6 +1927,10 @@
 	{NET_IPV6_RTR_SOLICIT_DELAY, "router_solicitation_delay",
          &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}},
 
Index: net/ipv6/af_inet6.c
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux24/net/ipv6/af_inet6.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.8.1
diff -u -r1.1.1.1 -r1.1.1.1.8.1
--- net/ipv6/af_inet6.c	2002/08/20 09:47:02	1.1.1.1
+++ net/ipv6/af_inet6.c	2002/09/11 03:30:27	1.1.1.1.8.1
@@ -173,6 +173,8 @@
 	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.
 	 */
@@ -248,6 +250,8 @@
 
 	/* Check if the address belongs to the host. */
 	if (addr_type == IPV6_ADDR_MAPPED) {
+		if (sk->net_pinfo.af_inet6.ipv6only)
+			return -EADDRNOTAVAIL;
 		v4addr = addr->sin6_addr.s6_addr32[3];
 		if (inet_addr_type(v4addr) != RTN_LOCAL)
 			return -EADDRNOTAVAIL;
Index: net/ipv6/ipv6_sockglue.c
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux24/net/ipv6/ipv6_sockglue.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.8.1
diff -u -r1.1.1.1 -r1.1.1.1.8.1
--- net/ipv6/ipv6_sockglue.c	2002/08/20 09:47:02	1.1.1.1
+++ net/ipv6/ipv6_sockglue.c	2002/09/11 03:30:27	1.1.1.1.8.1
@@ -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, 
@@ -520,6 +529,10 @@
 
 	case IPV6_FLOWINFO_SEND:
 		val = np->sndflow;
+		break;
+
+	case IPV6_V6ONLY:
+		val = np->ipv6only;
 		break;
 
 	default:
Index: net/ipv6/tcp_ipv6.c
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux24/net/ipv6/tcp_ipv6.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.8.1
diff -u -r1.1.1.1 -r1.1.1.1.8.1
--- net/ipv6/tcp_ipv6.c	2002/08/20 09:47:02	1.1.1.1
+++ net/ipv6/tcp_ipv6.c	2002/09/11 03:30:27	1.1.1.1.8.1
@@ -14,6 +14,14 @@
  *
  *	Fixes:
  *	Hideaki YOSHIFUJI	:	sin6_scope_id support
+ *	YOSHIFUJI Hideaki @USAGI:	Reworked bind(2) behavior, including:
+ *					- Allow ipv6 and ipv4 bind(2) to the
+ *					  same port if IPV6_V6ONLY socket
+ *					  option is set.
+ *					- Don't allow binding to the same
+ *					  address unless it is one of multi-
+ *					  cast address even if SO_REUSEADDR 
+ *					  is set.
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -137,28 +145,70 @@
 			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*)sk)->v6_rcv_saddr;
+			int addr_type = ipv6_addr_type(sk_rcv_saddr6),
+			    addr_type2;
+
+			sk_reuse = 0;
+			if (sk->reuse)
+				sk_reuse |= 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;
+				sk2_rcv_saddr6 = sk2->state != TCP_TIME_WAIT ?
+							&sk2->net_pinfo.af_inet6.rcv_saddr :
+							&((struct tcp_tw_bucket*)sk2)->v6_rcv_saddr;
+
+				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)) {
+					continue;
+				}
+
+				sk2_reuse = 0;
+				if (sk2->reuse)
+					sk2_reuse |= 1;
+
+				if (sk2_reuse & sk_reuse & 3) { /* NOT && */
+					ret = 1;
+					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;
@@ -601,6 +651,9 @@
 		struct sockaddr_in sin;
 
 		SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
+
+		if (sk->net_pinfo.af_inet6.ipv6only)
+			return -ENETUNREACH;
 
 		sin.sin_family = AF_INET;
 		sin.sin_port = usin->sin6_port;
Index: net/ipv6/udp.c
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux24/net/ipv6/udp.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.8.1
diff -u -r1.1.1.1 -r1.1.1.1.8.1
--- net/ipv6/udp.c	2002/08/20 09:47:02	1.1.1.1
+++ net/ipv6/udp.c	2002/09/11 03:30:27	1.1.1.1.8.1
@@ -11,7 +11,16 @@
  *
  *	Fixes:
  *	Hideaki YOSHIFUJI	:	sin6_scope_id support
+ *	YOSHIFUJI Hideaki @USAGI:	Reworked bind(2) behavior, including:
+ *					- Allow ipv6 and ipv4 bind(2) to the
+ *					  same port if IPV6_V6ONLY socket
+ *					  option is set.
+ *					- Don't allow binding to the same
+ *					  address unless it is one of multi-
+ *					  cast address even if SO_REUSEADDR 
+ *					  is set.
  *
+ *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
  *      as published by the Free Software Foundation; either version
@@ -98,23 +107,72 @@
 		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 |= 1;
+		if (sk_reuse &&
+		    (addr_type != IPV6_ADDR_MAPPED ? (addr_type & IPV6_ADDR_MULTICAST) : MULTICAST(sk->rcv_saddr)))
+			sk_reuse |= 4;
 
 		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;
+
+			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 |= 1;
+			if (sk2_reuse &&
+			    (addr_type2 != IPV6_ADDR_MAPPED ? (addr_type2 & IPV6_ADDR_MULTICAST) : MULTICAST(sk2->rcv_saddr)))
+				sk2_reuse |= 4;
+
+			if (sk2_reuse & sk_reuse & 3) {	/* NOT && */
+				if (sk2_reuse & sk_reuse & 4)
+					continue;
+				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;
 		}
 	}
 
@@ -221,6 +279,8 @@
 	int			err;
 
 	if (usin->sin6_family == AF_INET) {
+		if (sk->net_pinfo.af_inet6.ipv6only)
+			return -EAFNOSUPPORT;
 		err = udp_connect(sk, uaddr, addr_len);
 		goto ipv4_connected;
 	}
@@ -256,6 +316,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 = usin->sin6_port;
@@ -783,8 +846,11 @@
 	fl.oif = 0;
 
 	if (sin6) {
-		if (sin6->sin6_family == AF_INET)
+		if (sin6->sin6_family == AF_INET) {
+			if (sk->net_pinfo.af_inet6.ipv6only)
+				return -EAFNOSUPPORT;
 			return udp_sendmsg(sk, msg, ulen);
+		}
 
 		if (addr_len < SIN6_LEN_RFC2133)
 			return -EINVAL;
@@ -830,6 +896,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];

Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help