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];