--- 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);
+ }
+ }
+}