8216417: cleanup of IPv6 scope-id handling
authormichaelm
Thu, 13 Jun 2019 09:10:51 +0100
changeset 55375 96c7427456f9
parent 55374 5c4f1b7c753b
child 55376 0f38ab93a53c
8216417: cleanup of IPv6 scope-id handling Reviewed-by: alanb, chegar, dfuchs
src/java.base/share/classes/java/net/AbstractPlainDatagramSocketImpl.java
src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java
src/java.base/share/classes/java/net/Inet6Address.java
src/java.base/share/classes/sun/net/util/IPAddressUtil.java
src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java
src/java.base/share/classes/sun/nio/ch/Net.java
src/java.base/share/native/libnet/Inet6Address.c
src/java.base/share/native/libnet/net_util.h
src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java
src/java.base/unix/native/libnet/Inet6AddressImpl.c
src/java.base/unix/native/libnet/PlainDatagramSocketImpl.c
src/java.base/unix/native/libnet/PlainSocketImpl.c
src/java.base/unix/native/libnet/net_util_md.c
src/java.base/unix/native/libnet/net_util_md.h
src/java.base/windows/classes/java/net/DualStackPlainDatagramSocketImpl.java
src/java.base/windows/classes/java/net/TwoStacksPlainDatagramSocketImpl.java
src/java.base/windows/native/libnet/TwoStacksPlainDatagramSocketImpl.c
src/java.base/windows/native/libnet/net_util_md.c
test/jdk/ProblemList.txt
test/jdk/java/net/Inet6Address/Scoping.java
test/jdk/java/net/MulticastSocket/PromiscuousIPv6.java
test/jdk/java/net/ipv6tests/B6521014.java
test/jdk/java/nio/channels/DatagramChannel/PromiscuousIPv6.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;
--- 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;
     }
--- 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() {
--- 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<InetAddress,InetAddress>
+        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<List<InetAddress>> pa = () -> NetworkInterface.networkInterfaces()
+                .flatMap(NetworkInterface::inetAddresses)
+                .filter(a -> (a instanceof Inet6Address)
+                        && address.equals(a)
+                        && ((Inet6Address) a).getScopeId() != 0)
+                .collect(Collectors.toList());
+        List<InetAddress> 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;
+        }
+    }
 }
--- 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()) {
--- 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);
--- 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;");
--- 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;
--- 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;
 
--- 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
--- 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);
--- 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 ||
--- 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; i<nRoutes; i++, ptr++) {
-        struct in6_addr *target_addr=&ptr->addr;
-        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; i<nifs; i++, lif++) {
-        if (memcmp (addr, lif->localaddr, 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 :-
  *
--- 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__
--- 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)
--- 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;
 
--- 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];
--- 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);
         }
--- 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
--- /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<T> {
+        public void accept(T t) throws Exception;
+    }
+
+    static List<ThrowingConsumer<InetSocketAddress>> 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<Inet6Address> 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<InetSocketAddress> 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");
+        }
+    }
+}
--- /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<NetworkInterface> 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);
+        }
+    }
+}
--- 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();
     }
 
--- /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<NetworkInterface> 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);
+        }
+    }
+}