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

Pekka Savola (pekkas@netcore.fi)
Thu, 3 Oct 2002 10:00:00 +0300 (EEST)


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 <yoshfuji@linux-ipv6.org>
> Credit: YOSHIFUJI Hideaki / USAGI Project <yoshfuji@linux-ipv6.org>
> 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

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/