2002-10-03 03:08:27

by YOSHIFUJI Hideaki

[permalink] [raw]
Subject: [PATCH] IPv6: Allow Both IPv6 and IPv4 Sockets on the Same Port Number (IPV6_V6ONLY Support)

Hello!

Linux IPv6 stack provides the ability for IPv6 applications to
interoperate with IPv4 applications. Port space for TCP (or UDP) is
shared by IPv6 and IPv4. This conforms to RFC2553.
However, some kind of applications may want to restrict their use of
an IPv6 socket to IPv6 communication only. IPV6_V6ONLY socket option is
defined for such applications in RFC2553bis, which is successor of RFC2553.
This patch allows to bind both IPv6 and IPv4 sockets with the single
port number at the same time if IPV6_V6ONLY socket options is set to
the IPv6 socket.

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.

Packet delivery strategy is similar to one before, but we prefer
IPv4 a bit.

Following patch is against linux-2.4.19.

Thank you in advance.

-------------------------------------------------------------------
Patch-Name: Allow Both IPv6 and IPv4 Sockets on the Same Port Number (IPV6_V6ONLY Support)
Patch-Id: FIX_2_4_19_DOUBLEBIND-20020909
Patch-Author: YOSHIFUJI Hideaki / USAGI Project <[email protected]>
Credit: YOSHIFUJI Hideaki / USAGI Project <[email protected]>
Reference: RFC2553bis
-------------------------------------------------------------------
Index: include/linux/in6.h
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux24/include/linux/in6.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/linux/in6.h 2002/08/20 09:46:34 1.1.1.1
+++ include/linux/in6.h 2002/09/11 03:30:27 1.1.1.1.8.1
@@ -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
@@ -167,4 +168,19 @@
#define IPV6_FLOWINFO_SEND 33


+#ifdef __KERNEL__
+#ifndef IN6_IS_ADDR_UNSPECIFIED
+#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))
+#endif
+#ifndef IN6_IS_ADDR_V4MAPPED
+#define IN6_IS_ADDR_V4MAPPED(a) \
+ ((((a)->s6_addr32[0]) == 0) && \
+ (((a)->s6_addr32[1]) == 0) && \
+ (((a)->s6_addr32[2]) == __constant_htonl(0x0000ffff)))
+#endif
+#endif
#endif
Index: include/linux/sysctl.h
===================================================================
RCS file: /cvsroot/usagi/usagi-backport/linux24/include/linux/sysctl.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/linux/sysctl.h 2002/08/20 09:46:34 1.1.1.1
+++ include/linux/sysctl.h 2002/09/11 03:30:27 1.1.1.1.8.1
@@ -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> */
Index: include/net/if_inet6.h
===================================================================
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];


2002-10-03 03:47:55

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH] IPv6: Allow Both IPv6 and IPv4 Sockets on the Same Port Number (IPV6_V6ONLY Support)


Quoting YOSHIFUJI Hideaki / 吉藤英明 <[email protected]>:

> +#define IN6_IS_ADDR_V4MAPPED(a) \
> + ((((a)->s6_addr32[0]) == 0) && \
> + (((a)->s6_addr32[1]) == 0) && \
> + (((a)->s6_addr32[2]) == __constant_htonl(0x0000ffff)))

Please use plain htonl, __constant_htonl is only needed in static
initializations, in all other cases with constants as a parameter it
generates the same code as htonl, so lets prefer using the shorter,
more readable format.

- Arnaldo

2002-10-03 03:47:45

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH] IPv6: Allow Both IPv6 and IPv4 Sockets on the Same Port Number (IPV6_V6ONLY Support)


Quoting YOSHIFUJI Hideaki / 吉藤英明 <[email protected]>:

> +#define IN6_IS_ADDR_V4MAPPED(a) \
> + ((((a)->s6_addr32[0]) == 0) && \
> + (((a)->s6_addr32[1]) == 0) && \
> + (((a)->s6_addr32[2]) == __constant_htonl(0x0000ffff)))

Please use plain htonl, __constant_htonl is only needed in static
initializations, in all other cases with constants as a parameter it
generates the same code as htonl, so lets prefer using the shorter,
more readable format.

- Arnaldo

2002-10-03 03:49:55

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [PATCH] IPv6: Allow Both IPv6 and IPv4 Sockets on the Same Port Number (IPV6_V6ONLY Support)

Quoting YOSHIFUJI Hideaki / 吉藤英明 <[email protected]>:


> + (((a)->s6_addr32[2]) == __constant_htonl(0x0000ffff)))

Please use plain htonl, __constant_htonl is only needed in static
initializations, in all other cases with constants as a parameter it
generates the same code as htonl, so lets prefer using the shorter,
more readable format.

- Arnaldo

2002-10-03 06:55:13

by Pekka Savola

[permalink] [raw]
Subject: Re: [PATCH] IPv6: Allow Both IPv6 and IPv4 Sockets on the Same Port Number (IPV6_V6ONLY Support)

Please add a short description of 'bindv6only' in
Documentation/networking/ip-sysctl.txt.

This toggle seems usable only in interface "all" context.

Didn't really look at the rest of the patch.

On Thu, 3 Oct 2002, YOSHIFUJI Hideaki / [iso-2022-jp] 吉藤英明 wrote:
> Hello!
>
> Linux IPv6 stack provides the ability for IPv6 applications to
> interoperate with IPv4 applications. Port space for TCP (or UDP) is
> shared by IPv6 and IPv4. This conforms to RFC2553.
> However, some kind of applications may want to restrict their use of
> an IPv6 socket to IPv6 communication only. IPV6_V6ONLY socket option is
> defined for such applications in RFC2553bis, which is successor of RFC2553.
> This patch allows to bind both IPv6 and IPv4 sockets with the single
> port number at the same time if IPV6_V6ONLY socket options is set to
> the IPv6 socket.
>
> 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.
>
> Packet delivery strategy is similar to one before, but we prefer
> IPv4 a bit.
>
> Following patch is against linux-2.4.19.
>
> Thank you in advance.
>
> -------------------------------------------------------------------
> Patch-Name: Allow Both IPv6 and IPv4 Sockets on the Same Port Number (IPV6_V6ONLY Support)
> Patch-Id: FIX_2_4_19_DOUBLEBIND-20020909
> Patch-Author: YOSHIFUJI Hideaki / USAGI Project <[email protected]>
> Credit: YOSHIFUJI Hideaki / USAGI Project <[email protected]>
> Reference: RFC2553bis
> -------------------------------------------------------------------
> Index: include/linux/in6.h
> ===================================================================
> RCS file: /cvsroot/usagi/usagi-backport/linux24/include/linux/in6.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/linux/in6.h 2002/08/20 09:46:34 1.1.1.1
> +++ include/linux/in6.h 2002/09/11 03:30:27 1.1.1.1.8.1
> @@ -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
> @@ -167,4 +168,19 @@
> #define IPV6_FLOWINFO_SEND 33
>
>
> +#ifdef __KERNEL__
> +#ifndef IN6_IS_ADDR_UNSPECIFIED
> +#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))
> +#endif
> +#ifndef IN6_IS_ADDR_V4MAPPED
> +#define IN6_IS_ADDR_V4MAPPED(a) \
> + ((((a)->s6_addr32[0]) == 0) && \
> + (((a)->s6_addr32[1]) == 0) && \
> + (((a)->s6_addr32[2]) == __constant_htonl(0x0000ffff)))
> +#endif
> +#endif
> #endif
> Index: include/linux/sysctl.h
> ===================================================================
> RCS file: /cvsroot/usagi/usagi-backport/linux24/include/linux/sysctl.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/linux/sysctl.h 2002/08/20 09:46:34 1.1.1.1
> +++ include/linux/sysctl.h 2002/09/11 03:30:27 1.1.1.1.8.1
> @@ -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> */
> Index: include/net/if_inet6.h
> ===================================================================
> 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];
>
>

--
Pekka Savola "Tell me of difficulties surmounted,
Netcore Oy not those you stumble over and fall"
Systems. Networks. Security. -- Robert Jordan: A Crown of Swords

2002-10-03 08:30:49

by David Miller

[permalink] [raw]
Subject: Re: [PATCH] IPv6: Allow Both IPv6 and IPv4 Sockets on the Same Port Number (IPV6_V6ONLY Support)

From: YOSHIFUJI Hideaki / 吉藤英明 <[email protected]>
Date: Thu, 03 Oct 2002 12:13:50 +0900 (JST)

Linux IPv6 stack provides the ability for IPv6 applications to
interoperate with IPv4 applications. Port space for TCP (or UDP) is
shared by IPv6 and IPv4. This conforms to RFC2553.
However, some kind of applications may want to restrict their use of
an IPv6 socket to IPv6 communication only. IPV6_V6ONLY socket option is
defined for such applications in RFC2553bis, which is successor of RFC2553.

I really wish BSD socket features did not get standardized
in RFC's, we must live with their mistakes.

For example, this IPV6_V6ONLY socket option is flawed. What we
really need is a generic socket option which says "my family only"

There is nothing ipv6 specific about such a socket attribute.

So please, create instead "SO_ONEFAMILY" or similar generic
socket option.

I still need to review the rest of the patch for functional
correctness. This is probably the most complex area of the
socket identity code in TCP/UDP :-)

2002-10-03 08:57:03

by Brad Hards

[permalink] [raw]
Subject: Re: [PATCH] IPv6: Allow Both IPv6 and IPv4 Sockets on the Same Port Number (IPV6_V6ONLY Support)

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On Thu, 3 Oct 2002 18:29, David S. Miller wrote:
> For example, this IPV6_V6ONLY socket option is flawed. What we
> really need is a generic socket option which says "my family only"
>
> There is nothing ipv6 specific about such a socket attribute.
>
> So please, create instead "SO_ONEFAMILY" or similar generic
> socket option.
>
> I still need to review the rest of the patch for functional
> correctness. This is probably the most complex area of the
> socket identity code in TCP/UDP :-)
While you are grotting aroung in this area - a thought / request.

When we get IPv4 link-local autoconf addressing in widespread use, there is a
problem on multi-homed machines.

Assume B has two network interfaces (B1 and B2) on seperate IPv4 links (net1
and net2). Host A is on net1 and Host C is on net2. Assume that both Host A
and Host C have the same autoconf address. So IP address is not enough
information for Host B to use to determine which interface to use in order to
contact Host A (instead of Host C).

If host B has socket binding on IP+port+local interface, it all works out.

Is this going to work?

Brad

- --
http://conf.linux.org.au. 22-25Jan2003. Perth, Aust. Tickets booked.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.6 (GNU/Linux)
Comment: For info see http://www.gnupg.org

iD8DBQE9nAXwW6pHgIdAuOMRAscOAKC/TyYdV1IOjDMlYZghhLf1mYtrKgCfbDEh
VJAdPL1Rc1Z2uM6RCIgSYOE=
=JZGw
-----END PGP SIGNATURE-----

2002-10-03 09:01:02

by David Miller

[permalink] [raw]
Subject: Re: [PATCH] IPv6: Allow Both IPv6 and IPv4 Sockets on the Same Port Number (IPV6_V6ONLY Support)

From: Brad Hards <[email protected]>
Date: Thu, 3 Oct 2002 18:55:11 +1000

Assume B has two network interfaces (B1 and B2) on seperate IPv4 links (net1
and net2). Host A is on net1 and Host C is on net2. Assume that both Host A
and Host C have the same autoconf address. So IP address is not enough
information for Host B to use to determine which interface to use in order to
contact Host A (instead of Host C).

If host B has socket binding on IP+port+local interface, it all works out.

You want SO_BINDTODEVICE, which we have.