# HG changeset patch # User michaelm # Date 1560413451 -3600 # Node ID 96c7427456f998aca4f8ce3be2e700d3181a18d9 # Parent 5c4f1b7c753b2c66b638cb1876cba9b14fc775ba 8216417: cleanup of IPv6 scope-id handling Reviewed-by: alanb, chegar, dfuchs diff -r 5c4f1b7c753b -r 96c7427456f9 src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java --- a/src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java Thu Jun 13 12:22:28 2019 +0530 +++ b/src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java Thu Jun 13 09:10:51 2019 +0100 @@ -33,6 +33,7 @@ import sun.net.ResourceManager; import sun.net.ext.ExtendedSocketOptions; +import sun.net.util.IPAddressUtil; import sun.security.action.GetPropertyAction; /** @@ -110,6 +111,9 @@ */ protected synchronized void bind(int lport, InetAddress laddr) throws SocketException { + if (laddr.isLinkLocalAddress()) { + laddr = IPAddressUtil.toScopedAddress(laddr); + } bind0(lport, laddr); } @@ -121,7 +125,19 @@ * destination address to send the packet to. * @param p the packet to be sent. */ - protected abstract void send(DatagramPacket p) throws IOException; + protected void send(DatagramPacket p) throws IOException { + InetAddress orig = p.getAddress(); + if (orig.isLinkLocalAddress()) { + InetAddress scoped = IPAddressUtil.toScopedAddress(orig); + if (orig != scoped) { + p = new DatagramPacket(p.getData(), p.getOffset(), + p.getLength(), scoped, p.getPort()); + } + } + send0(p); + } + + protected abstract void send0(DatagramPacket p) throws IOException; /** * Connects a datagram socket to a remote destination. This associates the remote @@ -131,6 +147,9 @@ * @param port the remote port number */ protected void connect(InetAddress address, int port) throws SocketException { + if (address.isLinkLocalAddress()) { + address = IPAddressUtil.toScopedAddress(address); + } connect0(address, port); connectedAddress = address; connectedPort = port; diff -r 5c4f1b7c753b -r 96c7427456f9 src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java --- a/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java Thu Jun 13 12:22:28 2019 +0530 +++ b/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java Thu Jun 13 09:10:51 2019 +0100 @@ -43,6 +43,7 @@ import sun.net.PlatformSocketImpl; import sun.net.ResourceManager; import sun.net.ext.ExtendedSocketOptions; +import sun.net.util.IPAddressUtil; import sun.net.util.SocketExceptions; /** @@ -157,8 +158,12 @@ boolean connected = false; try { InetAddress address = InetAddress.getByName(host); + // recording this.address as supplied by caller before calling connect + this.address = address; this.port = port; - this.address = address; + if (address.isLinkLocalAddress()) { + address = IPAddressUtil.toScopedAddress(address); + } connectToAddress(address, port, timeout); connected = true; @@ -182,8 +187,12 @@ * @param port the specified port */ protected void connect(InetAddress address, int port) throws IOException { + // recording this.address as supplied by caller before calling connect + this.address = address; this.port = port; - this.address = address; + if (address.isLinkLocalAddress()) { + address = IPAddressUtil.toScopedAddress(address); + } try { connectToAddress(address, port, timeout); @@ -215,10 +224,14 @@ InetSocketAddress addr = (InetSocketAddress) address; if (addr.isUnresolved()) throw new UnknownHostException(addr.getHostName()); + // recording this.address as supplied by caller before calling connect + InetAddress ia = addr.getAddress(); + this.address = ia; this.port = addr.getPort(); - this.address = addr.getAddress(); - - connectToAddress(this.address, port, timeout); + if (ia.isLinkLocalAddress()) { + ia = IPAddressUtil.toScopedAddress(ia); + } + connectToAddress(ia, port, timeout); connected = true; } finally { if (!connected) { @@ -546,6 +559,9 @@ NetHooks.beforeTcpBind(fd, address, lport); } } + if (address.isLinkLocalAddress()) { + address = IPAddressUtil.toScopedAddress(address); + } socketBind(address, lport); isBound = true; } diff -r 5c4f1b7c753b -r 96c7427456f9 src/java.base/share/classes/java/net/Inet6Address.java --- a/src/java.base/share/classes/java/net/Inet6Address.java Thu Jun 13 12:22:28 2019 +0530 +++ b/src/java.base/share/classes/java/net/Inet6Address.java Thu Jun 13 09:10:51 2019 +0100 @@ -176,11 +176,6 @@ class Inet6Address extends InetAddress { static final int INADDRSZ = 16; - /* - * cached scope_id - for link-local address use only. - */ - private transient int cached_scope_id; // 0 - private class Inet6AddressHolder { private Inet6AddressHolder() { diff -r 5c4f1b7c753b -r 96c7427456f9 src/java.base/share/classes/sun/net/util/IPAddressUtil.java --- a/src/java.base/share/classes/sun/net/util/IPAddressUtil.java Thu Jun 13 12:22:28 2019 +0530 +++ b/src/java.base/share/classes/sun/net/util/IPAddressUtil.java Thu Jun 13 09:10:51 2019 +0100 @@ -25,6 +25,20 @@ package sun.net.util; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; +import java.security.PrivilegedActionException; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + public class IPAddressUtil { private static final int INADDR4SZ = 4; private static final int INADDR16SZ = 16; @@ -287,4 +301,75 @@ } return false; } + /** + * Mapping from unscoped local Inet(6)Address to the same address + * including the correct scope-id, determined from NetworkInterface. + */ + private final static ConcurrentHashMap + cache = new ConcurrentHashMap<>(); + + /** + * Returns a scoped version of the supplied local, link-local ipv6 address + * if that scope-id can be determined from local NetworkInterfaces. + * If the address already has a scope-id or if the address is not local, ipv6 + * or link local, then the original address is returned. + * + * @param addr + * @exception SocketException if the given ipv6 link local address is found + * on more than one local interface + * @return + */ + public static InetAddress toScopedAddress(InetAddress address) + throws SocketException { + + if (address instanceof Inet6Address && address.isLinkLocalAddress() + && ((Inet6Address) address).getScopeId() == 0) { + + InetAddress cached = null; + try { + cached = cache.computeIfAbsent(address, k -> findScopedAddress(k)); + } catch (UncheckedIOException e) { + throw (SocketException)e.getCause(); + } + return cached != null ? cached : address; + } else { + return address; + } + } + + /** + * Same as above for InetSocketAddress + */ + public static InetSocketAddress toScopedAddress(InetSocketAddress address) + throws SocketException { + InetAddress addr; + InetAddress orig = address.getAddress(); + if ((addr = toScopedAddress(orig)) == orig) { + return address; + } else { + return new InetSocketAddress(addr, address.getPort()); + } + } + + private static InetAddress findScopedAddress(InetAddress address) { + PrivilegedExceptionAction> pa = () -> NetworkInterface.networkInterfaces() + .flatMap(NetworkInterface::inetAddresses) + .filter(a -> (a instanceof Inet6Address) + && address.equals(a) + && ((Inet6Address) a).getScopeId() != 0) + .collect(Collectors.toList()); + List result; + try { + result = AccessController.doPrivileged(pa); + var sz = result.size(); + if (sz == 0) + return null; + if (sz > 1) + throw new UncheckedIOException(new SocketException( + "Duplicate link local addresses: must specify scope-id")); + return result.get(0); + } catch (PrivilegedActionException pae) { + return null; + } + } } diff -r 5c4f1b7c753b -r 96c7427456f9 src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java --- a/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java Thu Jun 13 12:22:28 2019 +0530 +++ b/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java Thu Jun 13 09:10:51 2019 +0100 @@ -57,6 +57,7 @@ import sun.net.ResourceManager; import sun.net.ext.ExtendedSocketOptions; +import sun.net.util.IPAddressUtil; /** * An implementation of DatagramChannels. @@ -527,14 +528,16 @@ } else { // not connected SecurityManager sm = System.getSecurityManager(); + InetAddress ia = isa.getAddress(); if (sm != null) { - InetAddress ia = isa.getAddress(); if (ia.isMulticastAddress()) { sm.checkMulticast(ia); } else { sm.checkConnect(ia.getHostAddress(), isa.getPort()); } } + if (ia.isLinkLocalAddress()) + isa = IPAddressUtil.toScopedAddress(isa); n = send(fd, src, isa); if (blocking) { while (IOStatus.okayToRetry(n) && isOpen()) { diff -r 5c4f1b7c753b -r 96c7427456f9 src/java.base/share/classes/sun/nio/ch/Net.java --- a/src/java.base/share/classes/sun/nio/ch/Net.java Thu Jun 13 12:22:28 2019 +0530 +++ b/src/java.base/share/classes/sun/nio/ch/Net.java Thu Jun 13 09:10:51 2019 +0100 @@ -50,6 +50,7 @@ import java.util.Enumeration; import sun.net.ext.ExtendedSocketOptions; +import sun.net.util.IPAddressUtil; import sun.security.action.GetPropertyAction; public class Net { @@ -462,6 +463,9 @@ { boolean preferIPv6 = isIPv6Available() && (family != StandardProtocolFamily.INET); + if (addr.isLinkLocalAddress()) { + addr = IPAddressUtil.toScopedAddress(addr); + } bind0(fd, preferIPv6, exclusiveBind, addr, port); } @@ -481,6 +485,9 @@ static int connect(ProtocolFamily family, FileDescriptor fd, InetAddress remote, int remotePort) throws IOException { + if (remote.isLinkLocalAddress()) { + remote = IPAddressUtil.toScopedAddress(remote); + } boolean preferIPv6 = isIPv6Available() && (family != StandardProtocolFamily.INET); return connect0(preferIPv6, fd, remote, remotePort); diff -r 5c4f1b7c753b -r 96c7427456f9 src/java.base/share/native/libnet/Inet6Address.c --- a/src/java.base/share/native/libnet/Inet6Address.c Thu Jun 13 12:22:28 2019 +0530 +++ b/src/java.base/share/native/libnet/Inet6Address.c Thu Jun 13 09:10:51 2019 +0100 @@ -37,7 +37,6 @@ jfieldID ia6_ipaddressID; jfieldID ia6_scopeidID; -jfieldID ia6_cachedscopeidID; jfieldID ia6_scopeidsetID; jfieldID ia6_scopeifnameID; jmethodID ia6_ctrID; @@ -65,8 +64,6 @@ CHECK_NULL(ia6_ipaddressID); ia6_scopeidID = (*env)->GetFieldID(env, ia6h_class, "scope_id", "I"); CHECK_NULL(ia6_scopeidID); - ia6_cachedscopeidID = (*env)->GetFieldID(env, ia6_class, "cached_scope_id", "I"); - CHECK_NULL(ia6_cachedscopeidID); ia6_scopeidsetID = (*env)->GetFieldID(env, ia6h_class, "scope_id_set", "Z"); CHECK_NULL(ia6_scopeidsetID); ia6_scopeifnameID = (*env)->GetFieldID(env, ia6h_class, "scope_ifname", "Ljava/net/NetworkInterface;"); diff -r 5c4f1b7c753b -r 96c7427456f9 src/java.base/share/native/libnet/net_util.h --- a/src/java.base/share/native/libnet/net_util.h Thu Jun 13 12:22:28 2019 +0530 +++ b/src/java.base/share/native/libnet/net_util.h Thu Jun 13 09:10:51 2019 +0100 @@ -107,7 +107,6 @@ extern jfieldID ia6_holder6ID; extern jfieldID ia6_ipaddressID; extern jfieldID ia6_scopeidID; -extern jfieldID ia6_cachedscopeidID; extern jfieldID ia6_scopeidsetID; extern jfieldID ia6_scopeifnameID; extern jmethodID ia6_ctrID; diff -r 5c4f1b7c753b -r 96c7427456f9 src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java --- a/src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java Thu Jun 13 12:22:28 2019 +0530 +++ b/src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java Thu Jun 13 09:10:51 2019 +0100 @@ -57,7 +57,7 @@ protected synchronized native void bind0(int lport, InetAddress laddr) throws SocketException; - protected native void send(DatagramPacket p) throws IOException; + protected native void send0(DatagramPacket p) throws IOException; protected synchronized native int peek(InetAddress i) throws IOException; diff -r 5c4f1b7c753b -r 96c7427456f9 src/java.base/unix/native/libnet/Inet6AddressImpl.c --- a/src/java.base/unix/native/libnet/Inet6AddressImpl.c Thu Jun 13 12:22:28 2019 +0530 +++ b/src/java.base/unix/native/libnet/Inet6AddressImpl.c Thu Jun 13 09:10:51 2019 +0100 @@ -703,10 +703,6 @@ sa.sa6.sin6_family = AF_INET6; if (scope > 0) { sa.sa6.sin6_scope_id = scope; -#if defined(__linux__) - } else { - sa.sa6.sin6_scope_id = getDefaultIPv6Interface(&sa.sa6.sin6_addr); -#endif } // load network interface address to SOCKETADDRESS, if specified diff -r 5c4f1b7c753b -r 96c7427456f9 src/java.base/unix/native/libnet/PlainDatagramSocketImpl.c --- a/src/java.base/unix/native/libnet/PlainDatagramSocketImpl.c Thu Jun 13 12:22:28 2019 +0530 +++ b/src/java.base/unix/native/libnet/PlainDatagramSocketImpl.c Thu Jun 13 09:10:51 2019 +0100 @@ -79,10 +79,6 @@ static jfieldID pdsi_connectedAddress; static jfieldID pdsi_connectedPort; -extern void setDefaultScopeID(JNIEnv *env, struct sockaddr *him); -extern int getDefaultScopeID(JNIEnv *env); - - /* * Returns a java.lang.Integer based on 'i' */ @@ -200,7 +196,6 @@ JNI_TRUE) != 0) { return; } - setDefaultScopeID(env, &sa.sa); if (NET_Bind(fd, &sa, len) < 0) { if (errno == EADDRINUSE || errno == EADDRNOTAVAIL || @@ -266,8 +261,6 @@ return; } - setDefaultScopeID(env, &rmtaddr.sa); - if (NET_Connect(fd, &rmtaddr.sa, len) == -1) { NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException", "Connect failed"); @@ -334,11 +327,11 @@ /* * Class: java_net_PlainDatagramSocketImpl - * Method: send + * Method: send0 * Signature: (Ljava/net/DatagramPacket;)V */ JNIEXPORT void JNICALL -Java_java_net_PlainDatagramSocketImpl_send(JNIEnv *env, jobject this, +Java_java_net_PlainDatagramSocketImpl_send0(JNIEnv *env, jobject this, jobject packet) { char BUF[MAX_BUFFER_LEN]; @@ -393,7 +386,6 @@ } rmtaddrP = &rmtaddr.sa; } - setDefaultScopeID(env, &rmtaddr.sa); if (packetBufferLen > MAX_BUFFER_LEN) { /* When JNI-ifying the JDK's IO routines, we turned @@ -2145,26 +2137,6 @@ NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed"); return; } - -#ifdef __linux__ - /* - * On 2.4.8+ if we join a group with the interface set to 0 - * then the kernel records the interface it decides. This causes - * subsequent leave groups to fail as there is no match. Thus we - * pick the interface if there is a matching route. - */ - if (index == 0) { - int rt_index = getDefaultIPv6Interface(&(mname6.ipv6mr_multiaddr)); - if (rt_index > 0) { - index = rt_index; - } - } -#endif -#ifdef MACOSX - if (family == AF_INET6 && index == 0) { - index = getDefaultScopeID(env); - } -#endif mname6.ipv6mr_interface = index; } else { jint idx = (*env)->GetIntField(env, niObj, ni_indexID); diff -r 5c4f1b7c753b -r 96c7427456f9 src/java.base/unix/native/libnet/PlainSocketImpl.c --- a/src/java.base/unix/native/libnet/PlainSocketImpl.c Thu Jun 13 12:22:28 2019 +0530 +++ b/src/java.base/unix/native/libnet/PlainSocketImpl.c Thu Jun 13 09:10:51 2019 +0100 @@ -46,8 +46,6 @@ jfieldID psi_fdLockID; jfieldID psi_closePendingID; -extern void setDefaultScopeID(JNIEnv *env, struct sockaddr *him); - /* * file descriptor used for dup2 */ @@ -261,7 +259,6 @@ JNI_TRUE) != 0) { return; } - setDefaultScopeID(env, &sa.sa); if (trafficClass != 0 && ipv6_available()) { NET_SetTrafficClass(&sa, trafficClass); @@ -509,7 +506,6 @@ &len, JNI_TRUE) != 0) { return; } - setDefaultScopeID(env, &sa.sa); if (NET_Bind(fd, &sa, len) < 0) { if (errno == EADDRINUSE || errno == EADDRNOTAVAIL || diff -r 5c4f1b7c753b -r 96c7427456f9 src/java.base/unix/native/libnet/net_util_md.c --- a/src/java.base/unix/native/libnet/net_util_md.c Thu Jun 13 12:22:28 2019 +0530 +++ b/src/java.base/unix/native/libnet/net_util_md.c Thu Jun 13 09:10:51 2019 +0100 @@ -452,285 +452,7 @@ } } -#if defined(__linux__) - -/* following code creates a list of addresses from the kernel - * routing table that are routed via the loopback address. - * We check all destination addresses against this table - * and override the scope_id field to use the relevant value for "lo" - * in order to work-around the Linux bug that prevents packets destined - * for certain local addresses from being sent via a physical interface. - */ - -struct loopback_route { - struct in6_addr addr; /* destination address */ - int plen; /* prefix length */ -}; - -static struct loopback_route *loRoutes = 0; -static int nRoutes = 0; /* number of routes */ -static int loRoutes_size = 16; /* initial size */ -static int lo_scope_id = 0; - -static void initLoopbackRoutes(); - -void printAddr (struct in6_addr *addr) { - int i; - for (i=0; i<16; i++) { - printf ("%02x", addr->s6_addr[i]); - } - printf ("\n"); -} - -static jboolean needsLoopbackRoute (struct in6_addr* dest_addr) { - int byte_count; - int extra_bits, i; - struct loopback_route *ptr; - - if (loRoutes == 0) { - initLoopbackRoutes(); - } - - for (ptr = loRoutes, i=0; iaddr; - int dest_plen = ptr->plen; - byte_count = dest_plen >> 3; - extra_bits = dest_plen & 0x3; - - if (byte_count > 0) { - if (memcmp(target_addr, dest_addr, byte_count)) { - continue; /* no match */ - } - } - - if (extra_bits > 0) { - unsigned char c1 = ((unsigned char *)target_addr)[byte_count]; - unsigned char c2 = ((unsigned char *)&dest_addr)[byte_count]; - unsigned char mask = 0xff << (8 - extra_bits); - if ((c1 & mask) != (c2 & mask)) { - continue; - } - } - return JNI_TRUE; - } - return JNI_FALSE; -} - - -static void initLoopbackRoutes() { - FILE *f; - char srcp[8][5]; - char hopp[8][5]; - int dest_plen, src_plen, use, refcnt, metric; - unsigned long flags; - char dest_str[40]; - struct in6_addr dest_addr; - char device[16]; - struct loopback_route *loRoutesTemp; - - if (loRoutes != 0) { - free (loRoutes); - } - loRoutes = calloc (loRoutes_size, sizeof(struct loopback_route)); - if (loRoutes == 0) { - return; - } - /* - * Scan /proc/net/ipv6_route looking for a matching - * route. - */ - if ((f = fopen("/proc/net/ipv6_route", "r")) == NULL) { - return ; - } - while (fscanf(f, "%4s%4s%4s%4s%4s%4s%4s%4s %02x " - "%4s%4s%4s%4s%4s%4s%4s%4s %02x " - "%4s%4s%4s%4s%4s%4s%4s%4s " - "%08x %08x %08x %08lx %8s", - dest_str, &dest_str[5], &dest_str[10], &dest_str[15], - &dest_str[20], &dest_str[25], &dest_str[30], &dest_str[35], - &dest_plen, - srcp[0], srcp[1], srcp[2], srcp[3], - srcp[4], srcp[5], srcp[6], srcp[7], - &src_plen, - hopp[0], hopp[1], hopp[2], hopp[3], - hopp[4], hopp[5], hopp[6], hopp[7], - &metric, &use, &refcnt, &flags, device) == 31) { - - /* - * Some routes should be ignored - */ - if ( (dest_plen < 0 || dest_plen > 128) || - (src_plen != 0) || - (flags & (RTF_POLICY | RTF_FLOW)) || - ((flags & RTF_REJECT) && dest_plen == 0) ) { - continue; - } - - /* - * Convert the destination address - */ - dest_str[4] = ':'; - dest_str[9] = ':'; - dest_str[14] = ':'; - dest_str[19] = ':'; - dest_str[24] = ':'; - dest_str[29] = ':'; - dest_str[34] = ':'; - dest_str[39] = '\0'; - - if (inet_pton(AF_INET6, dest_str, &dest_addr) < 0) { - /* not an Ipv6 address */ - continue; - } - if (strcmp(device, "lo") != 0) { - /* Not a loopback route */ - continue; - } else { - if (nRoutes == loRoutes_size) { - loRoutesTemp = realloc (loRoutes, loRoutes_size * - sizeof (struct loopback_route) * 2); - - if (loRoutesTemp == 0) { - free(loRoutes); - loRoutes = NULL; - nRoutes = 0; - fclose (f); - return; - } - loRoutes=loRoutesTemp; - loRoutes_size *= 2; - } - memcpy (&loRoutes[nRoutes].addr,&dest_addr,sizeof(struct in6_addr)); - loRoutes[nRoutes].plen = dest_plen; - nRoutes ++; - } - } - - fclose (f); - { - /* now find the scope_id for "lo" */ - - char devname[21]; - char addr6p[8][5]; - int plen, scope, dad_status, if_idx; - - if ((f = fopen("/proc/net/if_inet6", "r")) != NULL) { - while (fscanf(f, "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n", - addr6p[0], addr6p[1], addr6p[2], addr6p[3], - addr6p[4], addr6p[5], addr6p[6], addr6p[7], - &if_idx, &plen, &scope, &dad_status, devname) == 13) { - - if (strcmp(devname, "lo") == 0) { - /* - * Found - so just return the index - */ - fclose(f); - lo_scope_id = if_idx; - return; - } - } - fclose(f); - } - } -} - -/* - * Following is used for binding to local addresses. Equivalent - * to code above, for bind(). - */ - -struct localinterface { - int index; - char localaddr [16]; -}; - -static struct localinterface *localifs = 0; -static int localifsSize = 0; /* size of array */ -static int nifs = 0; /* number of entries used in array */ - -/* not thread safe: make sure called once from one thread */ - -static void initLocalIfs () { - FILE *f; - unsigned char staddr [16]; - char ifname [33]; - struct localinterface *lif=0; - struct localinterface *localifsTemp; - int index, x1, x2, x3; - unsigned int u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,ua,ub,uc,ud,ue,uf; - - if ((f = fopen("/proc/net/if_inet6", "r")) == NULL) { - return ; - } - while (fscanf (f, "%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x " - "%d %x %x %x %32s",&u0,&u1,&u2,&u3,&u4,&u5,&u6,&u7, - &u8,&u9,&ua,&ub,&uc,&ud,&ue,&uf, - &index, &x1, &x2, &x3, ifname) == 21) { - staddr[0] = (unsigned char)u0; - staddr[1] = (unsigned char)u1; - staddr[2] = (unsigned char)u2; - staddr[3] = (unsigned char)u3; - staddr[4] = (unsigned char)u4; - staddr[5] = (unsigned char)u5; - staddr[6] = (unsigned char)u6; - staddr[7] = (unsigned char)u7; - staddr[8] = (unsigned char)u8; - staddr[9] = (unsigned char)u9; - staddr[10] = (unsigned char)ua; - staddr[11] = (unsigned char)ub; - staddr[12] = (unsigned char)uc; - staddr[13] = (unsigned char)ud; - staddr[14] = (unsigned char)ue; - staddr[15] = (unsigned char)uf; - nifs ++; - if (nifs > localifsSize) { - localifsTemp = (struct localinterface *) realloc( - localifs, sizeof (struct localinterface)* (localifsSize+5)); - if (localifsTemp == 0) { - free(localifs); - localifs = 0; - localifsSize = 0; - nifs = 0; - fclose(f); - return; - } - localifs = localifsTemp; - lif = localifs + localifsSize; - localifsSize += 5; - } else { - lif ++; - } - memcpy (lif->localaddr, staddr, 16); - lif->index = index; - } - fclose (f); -} - -/* return the scope_id (interface index) of the - * interface corresponding to the given address - * returns 0 if no match found - */ - -static int getLocalScopeID (char *addr) { - struct localinterface *lif; - int i; - if (localifs == 0) { - initLocalIfs(); - } - for (i=0, lif=localifs; ilocaladdr, 16) == 0) { - return lif->index; - } - } - return 0; -} - -void platformInit () { - initLoopbackRoutes(); - initLocalIfs(); -} - -#elif defined(_AIX) +#if defined(_AIX) /* Initialize stubs for blocking I/O workarounds (see src/solaris/native/java/net/linux_close.c) */ extern void aix_close_init(); @@ -816,71 +538,12 @@ *len = sizeof(struct sockaddr_in6); } -#ifdef __linux__ - /* - * On Linux if we are connecting to a link-local address - * we need to specify the interface in the scope_id (2.4 kernel only) - * - * If the scope was cached then we use the cached value. If not cached but - * specified in the Inet6Address we use that, but we first check if the - * address needs to be routed via the loopback interface. In this case, - * we override the specified value with that of the loopback interface. - * If no cached value exists and no value was specified by user, then - * we try to determine a value from the routing table. In all these - * cases the used value is cached for further use. - */ - if (IN6_IS_ADDR_LINKLOCAL(&sa->sa6.sin6_addr)) { - unsigned int cached_scope_id = 0, scope_id = 0; - - if (ia6_cachedscopeidID) { - cached_scope_id = (int)(*env)->GetIntField(env, iaObj, ia6_cachedscopeidID); - /* if cached value exists then use it. Otherwise, check - * if scope is set in the address. - */ - if (!cached_scope_id) { - if (ia6_scopeidID) { - scope_id = getInet6Address_scopeid(env, iaObj); - } - if (scope_id != 0) { - /* check user-specified value for loopback case - * that needs to be overridden - */ - if (kernelIsV24() && needsLoopbackRoute(&sa->sa6.sin6_addr)) { - cached_scope_id = lo_scope_id; - (*env)->SetIntField(env, iaObj, ia6_cachedscopeidID, cached_scope_id); - } - } else { - /* - * Otherwise consult the IPv6 routing tables to - * try determine the appropriate interface. - */ - if (kernelIsV24()) { - cached_scope_id = getDefaultIPv6Interface(&sa->sa6.sin6_addr); - } else { - cached_scope_id = getLocalScopeID((char *)&(sa->sa6.sin6_addr)); - if (cached_scope_id == 0) { - cached_scope_id = getDefaultIPv6Interface(&sa->sa6.sin6_addr); - } - } - (*env)->SetIntField(env, iaObj, ia6_cachedscopeidID, cached_scope_id); - } - } - } - - /* - * If we have a scope_id use the extended form - * of sockaddr_in6. - */ - sa->sa6.sin6_scope_id = cached_scope_id == 0 ? scope_id : cached_scope_id; - } -#else /* handle scope_id */ if (family != java_net_InetAddress_IPv4) { if (ia6_scopeidID) { sa->sa6.sin6_scope_id = getInet6Address_scopeid(env, iaObj); } } -#endif } else { jint address; if (family != java_net_InetAddress_IPv4) { @@ -1015,158 +678,6 @@ } /* - * Determine the default interface for an IPv6 address. - * - * 1. Scans /proc/net/ipv6_route for a matching route - * (eg: fe80::/10 or a route for the specific address). - * This will tell us the interface to use (eg: "eth0"). - * - * 2. Lookup /proc/net/if_inet6 to map the interface - * name to an interface index. - * - * Returns :- - * -1 if error - * 0 if no matching interface - * >1 interface index to use for the link-local address. - */ -#if defined(__linux__) -int getDefaultIPv6Interface(struct in6_addr *target_addr) { - FILE *f; - char srcp[8][5]; - char hopp[8][5]; - int dest_plen, src_plen, use, refcnt, metric; - unsigned long flags; - char dest_str[40]; - struct in6_addr dest_addr; - char device[16]; - jboolean match = JNI_FALSE; - - /* - * Scan /proc/net/ipv6_route looking for a matching - * route. - */ - if ((f = fopen("/proc/net/ipv6_route", "r")) == NULL) { - return -1; - } - while (fscanf(f, "%4s%4s%4s%4s%4s%4s%4s%4s %02x " - "%4s%4s%4s%4s%4s%4s%4s%4s %02x " - "%4s%4s%4s%4s%4s%4s%4s%4s " - "%08x %08x %08x %08lx %8s", - dest_str, &dest_str[5], &dest_str[10], &dest_str[15], - &dest_str[20], &dest_str[25], &dest_str[30], &dest_str[35], - &dest_plen, - srcp[0], srcp[1], srcp[2], srcp[3], - srcp[4], srcp[5], srcp[6], srcp[7], - &src_plen, - hopp[0], hopp[1], hopp[2], hopp[3], - hopp[4], hopp[5], hopp[6], hopp[7], - &metric, &use, &refcnt, &flags, device) == 31) { - - /* - * Some routes should be ignored - */ - if ( (dest_plen < 0 || dest_plen > 128) || - (src_plen != 0) || - (flags & (RTF_POLICY | RTF_FLOW)) || - ((flags & RTF_REJECT) && dest_plen == 0) ) { - continue; - } - - /* - * Convert the destination address - */ - dest_str[4] = ':'; - dest_str[9] = ':'; - dest_str[14] = ':'; - dest_str[19] = ':'; - dest_str[24] = ':'; - dest_str[29] = ':'; - dest_str[34] = ':'; - dest_str[39] = '\0'; - - if (inet_pton(AF_INET6, dest_str, &dest_addr) < 0) { - /* not an Ipv6 address */ - continue; - } else { - /* - * The prefix len (dest_plen) indicates the number of bits we - * need to match on. - * - * dest_plen / 8 => number of bytes to match - * dest_plen % 8 => number of additional bits to match - * - * eg: fe80::/10 => match 1 byte + 2 additional bits in the - * next byte. - */ - int byte_count = dest_plen >> 3; - int extra_bits = dest_plen & 0x3; - - if (byte_count > 0) { - if (memcmp(target_addr, &dest_addr, byte_count)) { - continue; /* no match */ - } - } - - if (extra_bits > 0) { - unsigned char c1 = ((unsigned char *)target_addr)[byte_count]; - unsigned char c2 = ((unsigned char *)&dest_addr)[byte_count]; - unsigned char mask = 0xff << (8 - extra_bits); - if ((c1 & mask) != (c2 & mask)) { - continue; - } - } - - /* - * We have a match - */ - match = JNI_TRUE; - break; - } - } - fclose(f); - - /* - * If there's a match then we lookup the interface - * index. - */ - if (match) { - char devname[21]; - char addr6p[8][5]; - int plen, scope, dad_status, if_idx; - - if ((f = fopen("/proc/net/if_inet6", "r")) != NULL) { - while (fscanf(f, "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n", - addr6p[0], addr6p[1], addr6p[2], addr6p[3], - addr6p[4], addr6p[5], addr6p[6], addr6p[7], - &if_idx, &plen, &scope, &dad_status, devname) == 13) { - - if (strcmp(devname, device) == 0) { - /* - * Found - so just return the index - */ - fclose(f); - return if_idx; - } - } - fclose(f); - } else { - /* - * Couldn't open /proc/net/if_inet6 - */ - return -1; - } - } - - /* - * If we get here it means we didn't there wasn't any - * route or we couldn't get the index of the interface. - */ - return 0; -} -#endif - - -/* * Wrapper for getsockopt system routine - does any necessary * pre/post processing to deal with OS specific oddities :- * diff -r 5c4f1b7c753b -r 96c7427456f9 src/java.base/unix/native/libnet/net_util_md.h --- a/src/java.base/unix/native/libnet/net_util_md.h Thu Jun 13 12:22:28 2019 +0530 +++ b/src/java.base/unix/native/libnet/net_util_md.h Thu Jun 13 09:10:51 2019 +0100 @@ -101,7 +101,6 @@ #ifdef __linux__ int kernelIsV24(); -int getDefaultIPv6Interface(struct in6_addr *target_addr); #endif #ifdef __solaris__ diff -r 5c4f1b7c753b -r 96c7427456f9 src/java.base/windows/classes/java/net/DualStackPlainDatagramSocketImpl.java --- a/src/java.base/windows/classes/java/net/DualStackPlainDatagramSocketImpl.java Thu Jun 13 12:22:28 2019 +0530 +++ b/src/java.base/windows/classes/java/net/DualStackPlainDatagramSocketImpl.java Thu Jun 13 09:10:51 2019 +0100 @@ -124,7 +124,7 @@ socketReceiveOrPeekData(nativefd, p, timeout, connected, false /*receive*/); } - protected void send(DatagramPacket p) throws IOException { + protected void send0(DatagramPacket p) throws IOException { int nativefd = checkAndReturnNativeFD(); if (p == null) diff -r 5c4f1b7c753b -r 96c7427456f9 src/java.base/windows/classes/java/net/TwoStacksPlainDatagramSocketImpl.java --- a/src/java.base/windows/classes/java/net/TwoStacksPlainDatagramSocketImpl.java Thu Jun 13 12:22:28 2019 +0530 +++ b/src/java.base/windows/classes/java/net/TwoStacksPlainDatagramSocketImpl.java Thu Jun 13 09:10:51 2019 +0100 @@ -184,7 +184,7 @@ boolean exclBind) throws SocketException; - protected native void send(DatagramPacket p) throws IOException; + protected native void send0(DatagramPacket p) throws IOException; protected synchronized native int peek(InetAddress i) throws IOException; diff -r 5c4f1b7c753b -r 96c7427456f9 src/java.base/windows/native/libnet/TwoStacksPlainDatagramSocketImpl.c --- a/src/java.base/windows/native/libnet/TwoStacksPlainDatagramSocketImpl.c Thu Jun 13 12:22:28 2019 +0530 +++ b/src/java.base/windows/native/libnet/TwoStacksPlainDatagramSocketImpl.c Thu Jun 13 09:10:51 2019 +0100 @@ -415,11 +415,11 @@ /* * Class: java_net_TwoStacksPlainDatagramSocketImpl - * Method: send + * Method: send0 * Signature: (Ljava/net/DatagramPacket;)V */ JNIEXPORT void JNICALL -Java_java_net_TwoStacksPlainDatagramSocketImpl_send +Java_java_net_TwoStacksPlainDatagramSocketImpl_send0 (JNIEnv *env, jobject this, jobject packet) { char BUF[MAX_BUFFER_LEN]; diff -r 5c4f1b7c753b -r 96c7427456f9 src/java.base/windows/native/libnet/net_util_md.c --- a/src/java.base/windows/native/libnet/net_util_md.c Thu Jun 13 12:22:28 2019 +0530 +++ b/src/java.base/windows/native/libnet/net_util_md.c Thu Jun 13 09:10:51 2019 +0100 @@ -752,37 +752,6 @@ return 0; } -/* - * Determine the default interface for an IPv6 address. - * - * Returns :- - * 0 if error - * > 0 interface index to use - */ -jint getDefaultIPv6Interface(JNIEnv *env, struct sockaddr_in6 *target_addr) -{ - int ret; - DWORD b; - struct sockaddr_in6 route; - SOCKET fd = socket(AF_INET6, SOCK_STREAM, 0); - if (fd == INVALID_SOCKET) { - return 0; - } - - ret = WSAIoctl(fd, SIO_ROUTING_INTERFACE_QUERY, - (void *)target_addr, sizeof(struct sockaddr_in6), - (void *)&route, sizeof(struct sockaddr_in6), - &b, 0, 0); - if (ret == SOCKET_ERROR) { - // error - closesocket(fd); - return 0; - } else { - closesocket(fd); - return route.sin6_scope_id; - } -} - /** * Enables SIO_LOOPBACK_FAST_PATH */ @@ -820,7 +789,7 @@ { jbyte caddr[16]; jint address; - unsigned int scopeid = 0, cached_scope_id = 0; + unsigned int scopeid = 0; if (family == java_net_InetAddress_IPv4) { // convert to IPv4-mapped address @@ -842,19 +811,11 @@ } else { getInet6Address_ipaddress(env, iaObj, (char *)caddr); scopeid = getInet6Address_scopeid(env, iaObj); - cached_scope_id = (unsigned int)(*env)->GetIntField(env, iaObj, ia6_cachedscopeidID); } sa->sa6.sin6_port = (u_short)htons((u_short)port); memcpy((void *)&sa->sa6.sin6_addr, caddr, sizeof(struct in6_addr)); sa->sa6.sin6_family = AF_INET6; - if ((family == java_net_InetAddress_IPv6) && - IN6_IS_ADDR_LINKLOCAL(&sa->sa6.sin6_addr) && - (!scopeid && !cached_scope_id)) - { - cached_scope_id = getDefaultIPv6Interface(env, &sa->sa6); - (*env)->SetIntField(env, iaObj, ia6_cachedscopeidID, cached_scope_id); - } - sa->sa6.sin6_scope_id = scopeid == 0 ? cached_scope_id : scopeid; + sa->sa6.sin6_scope_id = scopeid; if (len != NULL) { *len = sizeof(struct sockaddr_in6); } diff -r 5c4f1b7c753b -r 96c7427456f9 test/jdk/ProblemList.txt --- a/test/jdk/ProblemList.txt Thu Jun 13 12:22:28 2019 +0530 +++ b/test/jdk/ProblemList.txt Thu Jun 13 09:10:51 2019 +0100 @@ -604,9 +604,6 @@ java/net/ServerSocket/AcceptInheritHandle.java 8211854 aix-ppc64 -java/net/Inet6Address/B6206527.java 8216417 macosx-all -java/net/ipv6tests/B6521014.java 8216417 macosx-all - ############################################################################ # jdk_nio diff -r 5c4f1b7c753b -r 96c7427456f9 test/jdk/java/net/Inet6Address/Scoping.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/net/Inet6Address/Scoping.java Thu Jun 13 09:10:51 2019 +0100 @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8216417 + * @summary cleanup of IPv6 scope-id handling + * @library /test/lib + * @build jdk.test.lib.NetworkConfiguration + * @run main/othervm Scoping + */ + +import java.io.IOException; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.DatagramChannel; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.Enumeration; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +import jdk.test.lib.NetworkConfiguration; + +public class Scoping { + + interface ThrowingConsumer { + public void accept(T t) throws Exception; + } + + static List> targets = List.of( + /* 0 */ (a) -> {Socket s = new Socket(); s.bind(a);s.close();}, + /* 1 */ (a) -> {ServerSocket s = new ServerSocket(); s.bind(a);s.close();}, + /* 2 */ (a) -> {DatagramSocket s = new DatagramSocket(null); s.bind(a); s.close();}, + /* 3 */ (a) -> {MulticastSocket s = new MulticastSocket(null); s.bind(a); s.close();}, + /* 4 */ (a) -> {SocketChannel s = SocketChannel.open(); s.bind(a);s.close();}, + /* 5 */ (a) -> {ServerSocketChannel s = ServerSocketChannel.open(); s.bind(a);s.close();}, + /* 6 */ (a) -> {DatagramChannel s = DatagramChannel.open(); s.bind(a); s.close();}, + /* 7 */ (a) -> {SocketChannel s = SocketChannel.open(); s.socket().bind(a);s.close();}, + /* 8 */ (a) -> {ServerSocketChannel s = ServerSocketChannel.open(); s.socket().bind(a);s.close();}, + /* 9 */ (a) -> {DatagramChannel s = DatagramChannel.open(); s.socket().bind(a); s.close();}, + /* 10 */ (a) -> {socketTest(a);}, + /* 11 */ (a) -> {dgSocketTest(a, false);}, + /* 12 */ (a) -> {dgSocketTest(a, true);}, + /* 13 */ (a) -> {dgChannelTest(a, false);}, + /* 14 */ (a) -> {dgChannelTest(a, true);} + ); + + static List getLinkLocalAddrs() throws IOException { + return NetworkConfiguration.probe() + .ip6Addresses() + .filter(Inet6Address::isLinkLocalAddress) + .collect(Collectors.toList()); + } + + static void compare(InetSocketAddress a, InetSocketAddress b) { + Inet6Address a1 = (Inet6Address)a.getAddress(); + Inet6Address b1 = (Inet6Address)b.getAddress(); + compare (a1, b1); + } + + static void compare(InetAddress a, InetAddress b) { + Inet6Address a1 = (Inet6Address)a; + Inet6Address b1 = (Inet6Address)b; + if (!a1.equals(b1)) { + System.out.printf("%s != %s\n", a1, b1); + throw new RuntimeException("Addresses not the same"); + } + + if (!a1.getHostAddress().equals(b1.getHostAddress())) { + System.out.printf("%s != %s\n", a1, b1); + if (a1.getScopeId() != b1.getScopeId()) + throw new RuntimeException("Scope ids not the same"); + } + } + + static void socketTest(InetSocketAddress a) throws Exception { + System.out.printf("socketTest: address %s\n", a); + try (ServerSocket server = new ServerSocket(0, 5, a.getAddress())) { + InetAddress saddr = server.getInetAddress(); + int port = server.getLocalPort(); + Socket client = new Socket(saddr, port); + compare(client.getInetAddress(), saddr); + try { + client.close(); + } catch (IOException e) {} + } + } + + static void dgSocketTest(InetSocketAddress a, boolean connect) throws Exception { + try (DatagramSocket s = new DatagramSocket(null)) { + System.out.println("dgSocketTest: " + a); + s.bind(a); + String t = "Hello world"; + DatagramPacket rx = new DatagramPacket(new byte[128], 128); + int port = s.getLocalPort(); + InetSocketAddress dest = new InetSocketAddress(a.getAddress(), port); + DatagramPacket tx = new DatagramPacket(t.getBytes("ISO8859_1"), t.length(), dest); + if (connect) { + s.connect(dest); + System.out.println("dgSocketTest: connect remote addr = " + s.getRemoteSocketAddress()); + compare(a, (InetSocketAddress)s.getRemoteSocketAddress()); + } + s.send(tx); + s.receive(rx); + String t1 = new String(rx.getData(), rx.getOffset(), rx.getLength(), "ISO8859_1"); + if (!t1.equals(t)) + throw new RuntimeException("Packets not equal"); + } + } + + static void dgChannelTest(InetSocketAddress a, boolean connect) throws Exception { + try (DatagramChannel s = DatagramChannel.open()) { + System.out.println("dgChannelTest: " + a); + s.bind(a); + String t = "Hello world"; + ByteBuffer rx = ByteBuffer.allocate(128); + int port = ((InetSocketAddress)s.getLocalAddress()).getPort(); + InetSocketAddress dest = new InetSocketAddress(a.getAddress(), port); + ByteBuffer tx = ByteBuffer.wrap(t.getBytes("ISO8859_1")); + if (connect) { + s.connect(dest); + System.out.println("dgChannelTest: connect remote addr = " + s.getRemoteAddress()); + compare(a, (InetSocketAddress)s.getRemoteAddress()); + } + s.send(tx, dest); + s.receive(rx); + rx.flip(); + String t1 = new String(rx.array(), 0, rx.limit(), "ISO8859_1"); + System.out.printf("rx : %s, data: %s\n", rx, t1); + if (!t1.equals(t)) + throw new RuntimeException("Packets not equal"); + } + } + + static Inet6Address stripScope(Inet6Address address) { + byte[] bytes = address.getAddress(); + InetAddress result = null; + try { + result = InetAddress.getByAddress(bytes); + } catch (UnknownHostException e) { + assert false; + } + return (Inet6Address)result; + } + + public static void main(String[] args) throws Exception { + for (Inet6Address address : getLinkLocalAddrs()) { + Inet6Address stripped = stripScope(address); + InetSocketAddress s1 = new InetSocketAddress(address, 0); + InetSocketAddress s2 = new InetSocketAddress(stripped, 0); + System.out.println("Trying: " + address); + int count = 0, success = 0; + for (ThrowingConsumer target : targets) { + try { + target.accept(s1); + System.out.println("target " + count + " OK"); + // if that succeeds try s2 (the actual test) + try { + target.accept(s2); + success++; + } catch (IOException ee) { + String msg = "Failed: " + s2.toString() + "count: " + Integer.toString(count); + throw new RuntimeException (msg); + } + } catch (IOException e) { + System.out.println(e.getMessage()); + // OK + } + count++; + } + System.out.println("Succeeded with " + success + " binds"); + } + } +} diff -r 5c4f1b7c753b -r 96c7427456f9 test/jdk/java/net/MulticastSocket/PromiscuousIPv6.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/net/MulticastSocket/PromiscuousIPv6.java Thu Jun 13 09:10:51 2019 +0100 @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + +/* + * @test + * @bug 8215294 + * @requires os.family == "linux" + * @library /test/lib + * @build jdk.test.lib.NetworkConfiguration + * PromiscuousIPv6 + * @run main/othervm PromiscuousIPv6 + * @key randomness + */ + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.Inet6Address; +import java.net.InetSocketAddress; +import java.net.MulticastSocket; +import java.net.NetworkInterface; +import java.net.SocketTimeoutException; +import java.net.StandardSocketOptions; +import java.util.List; +import java.util.Random; +import jdk.test.lib.NetworkConfiguration; +import jtreg.SkippedException; +import static java.lang.System.out; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.stream.Collectors.toList; + +/* + * This test was created as a clone of the Promiscuous test and adapted for + * IPv6 node-local and link-local multicast addresses on Linux. + */ +public class PromiscuousIPv6 { + + static final Random rand = new Random(); + + static final int TIMEOUT = 5 * 1000; // 5 secs + + static int sendDatagram(NetworkInterface nif, InetAddress group, int port) + throws IOException + { + try (MulticastSocket mc = new MulticastSocket()) { + mc.setOption(StandardSocketOptions.IP_MULTICAST_IF, nif); + + int id = rand.nextInt(); + byte[] msg = Integer.toString(id).getBytes(UTF_8); + DatagramPacket p = new DatagramPacket(msg, msg.length); + p.setAddress(group); + p.setPort(port); + + out.printf("Sending datagram to: %s/%d\n", group, port); + mc.send(p); + return id; + } + } + + static void receiveDatagram(DatagramSocket mc, boolean datagramExpected, int id) + throws IOException + { + byte[] ba = new byte[100]; + DatagramPacket p = new DatagramPacket(ba, ba.length); + try { + mc.receive(p); + int recvId = Integer.parseInt( + new String(p.getData(), 0, p.getLength(), UTF_8)); + if (datagramExpected) { + if (recvId != id) + throw new RuntimeException("Unexpected id, got " + recvId + + ", expected: " + id); + out.printf("Received message as expected, %s\n", p.getAddress()); + } else { + throw new RuntimeException("Unexpected message received, " + + p.getAddress()); + } + } catch (SocketTimeoutException e) { + if (datagramExpected) + throw new RuntimeException("Expected message not received, " + + e.getMessage()); + else + out.printf("Message not received, as expected\n"); + } + } + + static void test(NetworkInterface nif, InetAddress group1, InetAddress group2) + throws IOException + { + // Bind addresses should include the same network interface / scope, so + // as to not reply on the default route when there are multiple interfaces + InetAddress bindAddr1 = Inet6Address.getByAddress(null, group1.getAddress(), nif); + InetAddress bindAddr2 = Inet6Address.getByAddress(null, group2.getAddress(), nif); + + try (MulticastSocket mc1 = new MulticastSocket(new InetSocketAddress(bindAddr1, 0)); + MulticastSocket mc2 = new MulticastSocket(new InetSocketAddress(bindAddr2, mc1.getLocalPort()))) { + + final int port = mc1.getLocalPort(); + out.printf("Using port: %d\n", port); + + mc1.setSoTimeout(TIMEOUT); + mc2.setSoTimeout(TIMEOUT); + + mc1.joinGroup(new InetSocketAddress(group1, 0), nif); + out.printf("mc1 joined the MC group: %s\n", group1); + mc2.joinGroup(new InetSocketAddress(group2, 0), nif); + out.printf("mc2 joined the MC group: %s\n", group2); + + out.printf("Sending datagram to: %s/%d\n", group1, port); + int id = sendDatagram(nif, group1, port); + + // the packet should be received by mc1 only + receiveDatagram(mc1, true, id); + receiveDatagram(mc2, false, 0); + + + out.printf("Sending datagram to: %s/%d\n", group2, port); + id = sendDatagram(nif, group2, port); + + // the packet should be received by mc2 only + receiveDatagram(mc2, true, id); + receiveDatagram(mc1, false, 0); + + mc1.leaveGroup(new InetSocketAddress(group1, 0), nif); + mc2.leaveGroup(new InetSocketAddress(group2, 0), nif); + } + } + + public static void main(String args[]) throws IOException { + String os = System.getProperty("os.name"); + + if (!os.equals("Linux")) { + throw new SkippedException("This test should be run only on Linux"); + } else { + String osVersion = System.getProperty("os.version"); + String prefix = "3.10.0"; + if (osVersion.startsWith(prefix)) { + throw new SkippedException( + String.format("The behavior under test is known NOT to work on '%s' kernels", prefix)); + } + } + + NetworkConfiguration.printSystemConfiguration(System.out); + List nifs = NetworkConfiguration.probe() + .ip6MulticastInterfaces() + .collect(toList()); + + if (nifs.size() == 0) { + throw new SkippedException( + "No IPv6 interfaces that support multicast found"); + } + + InetAddress interfaceLocal1 = InetAddress.getByName("ff11::3.4.5.6"); + InetAddress interfaceLocal2 = InetAddress.getByName("ff11::5.6.7.8"); + + InetAddress linkLocal1 = InetAddress.getByName("ff12::4.5.6.7"); + InetAddress linkLocal2 = InetAddress.getByName("ff12::7.8.9.10"); + + for (NetworkInterface nif : nifs) { + test(nif, interfaceLocal1, interfaceLocal2); + test(nif, linkLocal1, linkLocal2); + } + } +} diff -r 5c4f1b7c753b -r 96c7427456f9 test/jdk/java/net/ipv6tests/B6521014.java --- a/test/jdk/java/net/ipv6tests/B6521014.java Thu Jun 13 12:22:28 2019 +0530 +++ b/test/jdk/java/net/ipv6tests/B6521014.java Thu Jun 13 09:10:51 2019 +0100 @@ -67,7 +67,6 @@ return NetworkConfiguration.probe() .ip6Addresses() .filter(Inet6Address::isLinkLocalAddress) - .map(B6521014::removeScope) .findFirst(); } diff -r 5c4f1b7c753b -r 96c7427456f9 test/jdk/java/nio/channels/DatagramChannel/PromiscuousIPv6.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/nio/channels/DatagramChannel/PromiscuousIPv6.java Thu Jun 13 09:10:51 2019 +0100 @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + +/* + * @test + * @bug 8215294 + * @requires os.family == "linux" + * @library /test/lib + * @build jdk.test.lib.NetworkConfiguration + * PromiscuousIPv6 + * @run main PromiscuousIPv6 + * @key randomness + */ + +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.net.*; +import java.util.*; +import java.io.IOException; +import jdk.test.lib.NetworkConfiguration; +import jtreg.SkippedException; +import static java.net.StandardProtocolFamily.*; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.stream.Collectors.toList; + +/* + * This test was created as a copy of the Promiscuous test and adapted for + * IPv6 node-local and link-local multicast addresses on Linux. + */ +public class PromiscuousIPv6 { + + static final Random rand = new Random(); + + static final ProtocolFamily UNSPEC = () -> "UNSPEC"; + + /** + * Sends a datagram to the given multicast group + */ + static int sendDatagram(NetworkInterface nif, + InetAddress group, + int port) + throws IOException + { + ProtocolFamily family = (group instanceof Inet6Address) ? INET6 : INET; + DatagramChannel dc = DatagramChannel.open(family) + .setOption(StandardSocketOptions.IP_MULTICAST_IF, nif); + int id = rand.nextInt(); + byte[] msg = Integer.toString(id).getBytes(UTF_8); + ByteBuffer buf = ByteBuffer.wrap(msg); + System.out.format("Send message -> group %s (id=0x%x)\n", + group.getHostAddress(), id); + dc.send(buf, new InetSocketAddress(group, port)); + dc.close(); + return id; + } + + /** + * Waits (with timeout) for datagram. The {@code datagramExpected} + * parameter indicates whether a datagram is expected, and if + * {@code true} then {@code id} is the identifier in the payload. + */ + static void receiveDatagram(DatagramChannel dc, + String name, + boolean datagramExpected, + int id) + throws IOException + { + System.out.println("Checking if received by " + name); + + Selector sel = Selector.open(); + dc.configureBlocking(false); + dc.register(sel, SelectionKey.OP_READ); + ByteBuffer buf = ByteBuffer.allocateDirect(100); + + try { + for (;;) { + System.out.println("Waiting to receive message"); + sel.select(5*1000); + SocketAddress sa = dc.receive(buf); + + // no datagram received + if (sa == null) { + if (datagramExpected) { + throw new RuntimeException("Expected message not received"); + } + System.out.println("No message received (correct)"); + return; + } + + // datagram received + + InetAddress sender = ((InetSocketAddress)sa).getAddress(); + buf.flip(); + byte[] bytes = new byte[buf.remaining()]; + buf.get(bytes); + String s = new String(bytes, "UTF-8"); + int receivedId = -1; + try { + receivedId = Integer.parseInt(s); + System.out.format("Received message from %s (id=0x%x)\n", + sender, receivedId); + } catch (NumberFormatException x) { + System.out.format("Received message from %s (msg=%s)\n", sender, s); + } + + if (!datagramExpected) { + if (receivedId == id) + throw new RuntimeException("Message not expected"); + System.out.println("Message ignored (has wrong id)"); + } else { + if (receivedId == id) { + System.out.println("Message expected"); + return; + } + System.out.println("Message ignored (wrong sender)"); + } + + sel.selectedKeys().clear(); + buf.rewind(); + } + } finally { + sel.close(); + } + } + + static void test(ProtocolFamily family, + NetworkInterface nif, + InetAddress group1, + InetAddress group2) + throws IOException + { + + System.out.format("%nTest family=%s%n", family.name()); + + // Bind addresses should include the same network interface / scope, so + // as to not reply on the default route when there are multiple interfaces + InetAddress bindAddr1 = Inet6Address.getByAddress(null, group1.getAddress(), nif); + InetAddress bindAddr2 = Inet6Address.getByAddress(null, group2.getAddress(), nif); + + DatagramChannel dc1 = (family == UNSPEC) ? + DatagramChannel.open() : DatagramChannel.open(family); + DatagramChannel dc2 = (family == UNSPEC) ? + DatagramChannel.open() : DatagramChannel.open(family); + + try { + dc1.setOption(StandardSocketOptions.SO_REUSEADDR, true); + dc2.setOption(StandardSocketOptions.SO_REUSEADDR, true); + + dc1.bind(new InetSocketAddress(bindAddr1, 0)); + int port = dc1.socket().getLocalPort(); + dc2.bind(new InetSocketAddress(bindAddr2, port)); + + System.out.format("dc1 joining [%s]:%d @ %s\n", + group1.getHostAddress(), port, nif.getName()); + System.out.format("dc2 joining [%s]:%d @ %s\n", + group2.getHostAddress(), port, nif.getName()); + + dc1.join(group1, nif); + dc2.join(group2, nif); + + int id = sendDatagram(nif, group1, port); + + receiveDatagram(dc1, "dc1", true, id); + receiveDatagram(dc2, "dc2", false, id); + + id = sendDatagram(nif, group2, port); + + receiveDatagram(dc1, "dc1", false, id); + receiveDatagram(dc2, "dc2", true, id); + + } finally { + dc1.close(); + dc2.close(); + } + } + + public static void main(String[] args) throws IOException { + + String os = System.getProperty("os.name"); + + if (!os.equals("Linux")) { + throw new SkippedException("This test should be run only on Linux"); + } else { + String osVersion = System.getProperty("os.version"); + String prefix = "3.10.0"; + if (osVersion.startsWith(prefix)) { + throw new SkippedException( + String.format("The behavior under test is known NOT to work on '%s' kernels", prefix)); + } + } + + NetworkConfiguration.printSystemConfiguration(System.out); + List nifs = NetworkConfiguration.probe() + .ip6MulticastInterfaces() + .collect(toList()); + + if (nifs.size() == 0) { + throw new SkippedException( + "No IPv6 interfaces that support multicast found"); + } + + InetAddress interfaceLocal1 = InetAddress.getByName("ff11::2.3.4.5"); + InetAddress interfaceLocal2 = InetAddress.getByName("ff11::6.7.8.9"); + + InetAddress linkLocal1 = InetAddress.getByName("ff12::2.3.4.5"); + InetAddress linkLocal2 = InetAddress.getByName("ff12::6.7.8.9"); + + for (NetworkInterface nif : nifs) { + test(INET6, nif, interfaceLocal1, interfaceLocal2); + test(INET6, nif, linkLocal1, linkLocal2); + } + } +}