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