8216417: cleanup of IPv6 scope-id handling

Reviewed-by: alanb, chegar, dfuchs
This commit is contained in:
Michael McMahon 2019-06-13 09:10:51 +01:00
parent 04ea6ae9b3
commit 247a6a2ce4
23 changed files with 761 additions and 595 deletions

View file

@ -33,6 +33,7 @@ import java.util.Set;
import sun.net.ResourceManager; import sun.net.ResourceManager;
import sun.net.ext.ExtendedSocketOptions; import sun.net.ext.ExtendedSocketOptions;
import sun.net.util.IPAddressUtil;
import sun.security.action.GetPropertyAction; import sun.security.action.GetPropertyAction;
/** /**
@ -110,6 +111,9 @@ abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
*/ */
protected synchronized void bind(int lport, InetAddress laddr) protected synchronized void bind(int lport, InetAddress laddr)
throws SocketException { throws SocketException {
if (laddr.isLinkLocalAddress()) {
laddr = IPAddressUtil.toScopedAddress(laddr);
}
bind0(lport, laddr); bind0(lport, laddr);
} }
@ -121,7 +125,19 @@ abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
* destination address to send the packet to. * destination address to send the packet to.
* @param p the packet to be sent. * @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 * Connects a datagram socket to a remote destination. This associates the remote
@ -131,6 +147,9 @@ abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
* @param port the remote port number * @param port the remote port number
*/ */
protected void connect(InetAddress address, int port) throws SocketException { protected void connect(InetAddress address, int port) throws SocketException {
if (address.isLinkLocalAddress()) {
address = IPAddressUtil.toScopedAddress(address);
}
connect0(address, port); connect0(address, port);
connectedAddress = address; connectedAddress = address;
connectedPort = port; connectedPort = port;

View file

@ -43,6 +43,7 @@ import sun.net.NetHooks;
import sun.net.PlatformSocketImpl; import sun.net.PlatformSocketImpl;
import sun.net.ResourceManager; import sun.net.ResourceManager;
import sun.net.ext.ExtendedSocketOptions; import sun.net.ext.ExtendedSocketOptions;
import sun.net.util.IPAddressUtil;
import sun.net.util.SocketExceptions; import sun.net.util.SocketExceptions;
/** /**
@ -157,8 +158,12 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
boolean connected = false; boolean connected = false;
try { try {
InetAddress address = InetAddress.getByName(host); InetAddress address = InetAddress.getByName(host);
this.port = port; // recording this.address as supplied by caller before calling connect
this.address = address; this.address = address;
this.port = port;
if (address.isLinkLocalAddress()) {
address = IPAddressUtil.toScopedAddress(address);
}
connectToAddress(address, port, timeout); connectToAddress(address, port, timeout);
connected = true; connected = true;
@ -182,8 +187,12 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
* @param port the specified port * @param port the specified port
*/ */
protected void connect(InetAddress address, int port) throws IOException { protected void connect(InetAddress address, int port) throws IOException {
this.port = port; // recording this.address as supplied by caller before calling connect
this.address = address; this.address = address;
this.port = port;
if (address.isLinkLocalAddress()) {
address = IPAddressUtil.toScopedAddress(address);
}
try { try {
connectToAddress(address, port, timeout); connectToAddress(address, port, timeout);
@ -215,10 +224,14 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
InetSocketAddress addr = (InetSocketAddress) address; InetSocketAddress addr = (InetSocketAddress) address;
if (addr.isUnresolved()) if (addr.isUnresolved())
throw new UnknownHostException(addr.getHostName()); 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.port = addr.getPort();
this.address = addr.getAddress(); if (ia.isLinkLocalAddress()) {
ia = IPAddressUtil.toScopedAddress(ia);
connectToAddress(this.address, port, timeout); }
connectToAddress(ia, port, timeout);
connected = true; connected = true;
} finally { } finally {
if (!connected) { if (!connected) {
@ -546,6 +559,9 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
NetHooks.beforeTcpBind(fd, address, lport); NetHooks.beforeTcpBind(fd, address, lport);
} }
} }
if (address.isLinkLocalAddress()) {
address = IPAddressUtil.toScopedAddress(address);
}
socketBind(address, lport); socketBind(address, lport);
isBound = true; isBound = true;
} }

View file

@ -176,11 +176,6 @@ public final
class Inet6Address extends InetAddress { class Inet6Address extends InetAddress {
static final int INADDRSZ = 16; 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 class Inet6AddressHolder {
private Inet6AddressHolder() { private Inet6AddressHolder() {

View file

@ -25,6 +25,20 @@
package sun.net.util; 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 { public class IPAddressUtil {
private static final int INADDR4SZ = 4; private static final int INADDR4SZ = 4;
private static final int INADDR16SZ = 16; private static final int INADDR16SZ = 16;
@ -287,4 +301,75 @@ public class IPAddressUtil {
} }
return false; 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;
}
}
} }

View file

@ -57,6 +57,7 @@ import java.util.concurrent.locks.ReentrantLock;
import sun.net.ResourceManager; import sun.net.ResourceManager;
import sun.net.ext.ExtendedSocketOptions; import sun.net.ext.ExtendedSocketOptions;
import sun.net.util.IPAddressUtil;
/** /**
* An implementation of DatagramChannels. * An implementation of DatagramChannels.
@ -527,14 +528,16 @@ class DatagramChannelImpl
} else { } else {
// not connected // not connected
SecurityManager sm = System.getSecurityManager(); SecurityManager sm = System.getSecurityManager();
if (sm != null) {
InetAddress ia = isa.getAddress(); InetAddress ia = isa.getAddress();
if (sm != null) {
if (ia.isMulticastAddress()) { if (ia.isMulticastAddress()) {
sm.checkMulticast(ia); sm.checkMulticast(ia);
} else { } else {
sm.checkConnect(ia.getHostAddress(), isa.getPort()); sm.checkConnect(ia.getHostAddress(), isa.getPort());
} }
} }
if (ia.isLinkLocalAddress())
isa = IPAddressUtil.toScopedAddress(isa);
n = send(fd, src, isa); n = send(fd, src, isa);
if (blocking) { if (blocking) {
while (IOStatus.okayToRetry(n) && isOpen()) { while (IOStatus.okayToRetry(n) && isOpen()) {

View file

@ -50,6 +50,7 @@ import java.security.PrivilegedAction;
import java.util.Enumeration; import java.util.Enumeration;
import sun.net.ext.ExtendedSocketOptions; import sun.net.ext.ExtendedSocketOptions;
import sun.net.util.IPAddressUtil;
import sun.security.action.GetPropertyAction; import sun.security.action.GetPropertyAction;
public class Net { public class Net {
@ -462,6 +463,9 @@ public class Net {
{ {
boolean preferIPv6 = isIPv6Available() && boolean preferIPv6 = isIPv6Available() &&
(family != StandardProtocolFamily.INET); (family != StandardProtocolFamily.INET);
if (addr.isLinkLocalAddress()) {
addr = IPAddressUtil.toScopedAddress(addr);
}
bind0(fd, preferIPv6, exclusiveBind, addr, port); bind0(fd, preferIPv6, exclusiveBind, addr, port);
} }
@ -481,6 +485,9 @@ public class Net {
static int connect(ProtocolFamily family, FileDescriptor fd, InetAddress remote, int remotePort) static int connect(ProtocolFamily family, FileDescriptor fd, InetAddress remote, int remotePort)
throws IOException throws IOException
{ {
if (remote.isLinkLocalAddress()) {
remote = IPAddressUtil.toScopedAddress(remote);
}
boolean preferIPv6 = isIPv6Available() && boolean preferIPv6 = isIPv6Available() &&
(family != StandardProtocolFamily.INET); (family != StandardProtocolFamily.INET);
return connect0(preferIPv6, fd, remote, remotePort); return connect0(preferIPv6, fd, remote, remotePort);

View file

@ -37,7 +37,6 @@ jfieldID ia6_holder6ID;
jfieldID ia6_ipaddressID; jfieldID ia6_ipaddressID;
jfieldID ia6_scopeidID; jfieldID ia6_scopeidID;
jfieldID ia6_cachedscopeidID;
jfieldID ia6_scopeidsetID; jfieldID ia6_scopeidsetID;
jfieldID ia6_scopeifnameID; jfieldID ia6_scopeifnameID;
jmethodID ia6_ctrID; jmethodID ia6_ctrID;
@ -65,8 +64,6 @@ Java_java_net_Inet6Address_init(JNIEnv *env, jclass cls) {
CHECK_NULL(ia6_ipaddressID); CHECK_NULL(ia6_ipaddressID);
ia6_scopeidID = (*env)->GetFieldID(env, ia6h_class, "scope_id", "I"); ia6_scopeidID = (*env)->GetFieldID(env, ia6h_class, "scope_id", "I");
CHECK_NULL(ia6_scopeidID); 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"); ia6_scopeidsetID = (*env)->GetFieldID(env, ia6h_class, "scope_id_set", "Z");
CHECK_NULL(ia6_scopeidsetID); CHECK_NULL(ia6_scopeidsetID);
ia6_scopeifnameID = (*env)->GetFieldID(env, ia6h_class, "scope_ifname", "Ljava/net/NetworkInterface;"); ia6_scopeifnameID = (*env)->GetFieldID(env, ia6h_class, "scope_ifname", "Ljava/net/NetworkInterface;");

View file

@ -107,7 +107,6 @@ extern jclass ia6_class;
extern jfieldID ia6_holder6ID; extern jfieldID ia6_holder6ID;
extern jfieldID ia6_ipaddressID; extern jfieldID ia6_ipaddressID;
extern jfieldID ia6_scopeidID; extern jfieldID ia6_scopeidID;
extern jfieldID ia6_cachedscopeidID;
extern jfieldID ia6_scopeidsetID; extern jfieldID ia6_scopeidsetID;
extern jfieldID ia6_scopeifnameID; extern jfieldID ia6_scopeifnameID;
extern jmethodID ia6_ctrID; extern jmethodID ia6_ctrID;

View file

@ -57,7 +57,7 @@ class PlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl
protected synchronized native void bind0(int lport, InetAddress laddr) protected synchronized native void bind0(int lport, InetAddress laddr)
throws SocketException; 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; protected synchronized native int peek(InetAddress i) throws IOException;

View file

@ -703,10 +703,6 @@ Java_java_net_Inet6AddressImpl_isReachable0(JNIEnv *env, jobject this,
sa.sa6.sin6_family = AF_INET6; sa.sa6.sin6_family = AF_INET6;
if (scope > 0) { if (scope > 0) {
sa.sa6.sin6_scope_id = scope; 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 // load network interface address to SOCKETADDRESS, if specified

View file

@ -79,10 +79,6 @@ static jfieldID pdsi_connected;
static jfieldID pdsi_connectedAddress; static jfieldID pdsi_connectedAddress;
static jfieldID pdsi_connectedPort; 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' * Returns a java.lang.Integer based on 'i'
*/ */
@ -200,7 +196,6 @@ Java_java_net_PlainDatagramSocketImpl_bind0(JNIEnv *env, jobject this,
JNI_TRUE) != 0) { JNI_TRUE) != 0) {
return; return;
} }
setDefaultScopeID(env, &sa.sa);
if (NET_Bind(fd, &sa, len) < 0) { if (NET_Bind(fd, &sa, len) < 0) {
if (errno == EADDRINUSE || errno == EADDRNOTAVAIL || if (errno == EADDRINUSE || errno == EADDRNOTAVAIL ||
@ -266,8 +261,6 @@ Java_java_net_PlainDatagramSocketImpl_connect0(JNIEnv *env, jobject this,
return; return;
} }
setDefaultScopeID(env, &rmtaddr.sa);
if (NET_Connect(fd, &rmtaddr.sa, len) == -1) { if (NET_Connect(fd, &rmtaddr.sa, len) == -1) {
NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException", NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "ConnectException",
"Connect failed"); "Connect failed");
@ -334,11 +327,11 @@ Java_java_net_PlainDatagramSocketImpl_disconnect0(JNIEnv *env, jobject this, jin
/* /*
* Class: java_net_PlainDatagramSocketImpl * Class: java_net_PlainDatagramSocketImpl
* Method: send * Method: send0
* Signature: (Ljava/net/DatagramPacket;)V * Signature: (Ljava/net/DatagramPacket;)V
*/ */
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_java_net_PlainDatagramSocketImpl_send(JNIEnv *env, jobject this, Java_java_net_PlainDatagramSocketImpl_send0(JNIEnv *env, jobject this,
jobject packet) { jobject packet) {
char BUF[MAX_BUFFER_LEN]; char BUF[MAX_BUFFER_LEN];
@ -393,7 +386,6 @@ Java_java_net_PlainDatagramSocketImpl_send(JNIEnv *env, jobject this,
} }
rmtaddrP = &rmtaddr.sa; rmtaddrP = &rmtaddr.sa;
} }
setDefaultScopeID(env, &rmtaddr.sa);
if (packetBufferLen > MAX_BUFFER_LEN) { if (packetBufferLen > MAX_BUFFER_LEN) {
/* When JNI-ifying the JDK's IO routines, we turned /* When JNI-ifying the JDK's IO routines, we turned
@ -2145,26 +2137,6 @@ static void mcast_join_leave(JNIEnv *env, jobject this,
NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed"); NET_ThrowCurrent(env, "getsockopt IPV6_MULTICAST_IF failed");
return; 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; mname6.ipv6mr_interface = index;
} else { } else {
jint idx = (*env)->GetIntField(env, niObj, ni_indexID); jint idx = (*env)->GetIntField(env, niObj, ni_indexID);

View file

@ -46,8 +46,6 @@ jfieldID psi_trafficClassID;
jfieldID psi_fdLockID; jfieldID psi_fdLockID;
jfieldID psi_closePendingID; jfieldID psi_closePendingID;
extern void setDefaultScopeID(JNIEnv *env, struct sockaddr *him);
/* /*
* file descriptor used for dup2 * file descriptor used for dup2
*/ */
@ -261,7 +259,6 @@ Java_java_net_PlainSocketImpl_socketConnect(JNIEnv *env, jobject this,
JNI_TRUE) != 0) { JNI_TRUE) != 0) {
return; return;
} }
setDefaultScopeID(env, &sa.sa);
if (trafficClass != 0 && ipv6_available()) { if (trafficClass != 0 && ipv6_available()) {
NET_SetTrafficClass(&sa, trafficClass); NET_SetTrafficClass(&sa, trafficClass);
@ -509,7 +506,6 @@ Java_java_net_PlainSocketImpl_socketBind(JNIEnv *env, jobject this,
&len, JNI_TRUE) != 0) { &len, JNI_TRUE) != 0) {
return; return;
} }
setDefaultScopeID(env, &sa.sa);
if (NET_Bind(fd, &sa, len) < 0) { if (NET_Bind(fd, &sa, len) < 0) {
if (errno == EADDRINUSE || errno == EADDRNOTAVAIL || if (errno == EADDRINUSE || errno == EADDRNOTAVAIL ||

View file

@ -452,285 +452,7 @@ void NET_ThrowUnknownHostExceptionWithGaiError(JNIEnv *env,
} }
} }
#if defined(__linux__) #if defined(_AIX)
/* 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)
/* Initialize stubs for blocking I/O workarounds (see src/solaris/native/java/net/linux_close.c) */ /* Initialize stubs for blocking I/O workarounds (see src/solaris/native/java/net/linux_close.c) */
extern void aix_close_init(); extern void aix_close_init();
@ -816,71 +538,12 @@ NET_InetAddressToSockaddr(JNIEnv *env, jobject iaObj, int port,
*len = sizeof(struct sockaddr_in6); *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 */ /* handle scope_id */
if (family != java_net_InetAddress_IPv4) { if (family != java_net_InetAddress_IPv4) {
if (ia6_scopeidID) { if (ia6_scopeidID) {
sa->sa6.sin6_scope_id = getInet6Address_scopeid(env, iaObj); sa->sa6.sin6_scope_id = getInet6Address_scopeid(env, iaObj);
} }
} }
#endif
} else { } else {
jint address; jint address;
if (family != java_net_InetAddress_IPv4) { if (family != java_net_InetAddress_IPv4) {
@ -1014,158 +677,6 @@ NET_MapSocketOption(jint cmd, int *level, int *optname) {
return -1; return -1;
} }
/*
* 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 * Wrapper for getsockopt system routine - does any necessary
* pre/post processing to deal with OS specific oddities :- * pre/post processing to deal with OS specific oddities :-

View file

@ -101,7 +101,6 @@ void NET_SetTrafficClass(SOCKETADDRESS *sa, int trafficClass);
#ifdef __linux__ #ifdef __linux__
int kernelIsV24(); int kernelIsV24();
int getDefaultIPv6Interface(struct in6_addr *target_addr);
#endif #endif
#ifdef __solaris__ #ifdef __solaris__

View file

@ -124,7 +124,7 @@ class DualStackPlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl
socketReceiveOrPeekData(nativefd, p, timeout, connected, false /*receive*/); socketReceiveOrPeekData(nativefd, p, timeout, connected, false /*receive*/);
} }
protected void send(DatagramPacket p) throws IOException { protected void send0(DatagramPacket p) throws IOException {
int nativefd = checkAndReturnNativeFD(); int nativefd = checkAndReturnNativeFD();
if (p == null) if (p == null)

View file

@ -184,7 +184,7 @@ final class TwoStacksPlainDatagramSocketImpl extends AbstractPlainDatagramSocket
boolean exclBind) boolean exclBind)
throws SocketException; 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; protected synchronized native int peek(InetAddress i) throws IOException;

View file

@ -415,11 +415,11 @@ Java_java_net_TwoStacksPlainDatagramSocketImpl_disconnect0(JNIEnv *env, jobject
/* /*
* Class: java_net_TwoStacksPlainDatagramSocketImpl * Class: java_net_TwoStacksPlainDatagramSocketImpl
* Method: send * Method: send0
* Signature: (Ljava/net/DatagramPacket;)V * Signature: (Ljava/net/DatagramPacket;)V
*/ */
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_java_net_TwoStacksPlainDatagramSocketImpl_send Java_java_net_TwoStacksPlainDatagramSocketImpl_send0
(JNIEnv *env, jobject this, jobject packet) (JNIEnv *env, jobject this, jobject packet)
{ {
char BUF[MAX_BUFFER_LEN]; char BUF[MAX_BUFFER_LEN];

View file

@ -752,37 +752,6 @@ NET_BindV6(struct ipv6bind *b, jboolean exclBind) {
return 0; 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 * Enables SIO_LOOPBACK_FAST_PATH
*/ */
@ -820,7 +789,7 @@ NET_InetAddressToSockaddr(JNIEnv *env, jobject iaObj, int port,
{ {
jbyte caddr[16]; jbyte caddr[16];
jint address; jint address;
unsigned int scopeid = 0, cached_scope_id = 0; unsigned int scopeid = 0;
if (family == java_net_InetAddress_IPv4) { if (family == java_net_InetAddress_IPv4) {
// convert to IPv4-mapped address // convert to IPv4-mapped address
@ -842,19 +811,11 @@ NET_InetAddressToSockaddr(JNIEnv *env, jobject iaObj, int port,
} else { } else {
getInet6Address_ipaddress(env, iaObj, (char *)caddr); getInet6Address_ipaddress(env, iaObj, (char *)caddr);
scopeid = getInet6Address_scopeid(env, iaObj); 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); sa->sa6.sin6_port = (u_short)htons((u_short)port);
memcpy((void *)&sa->sa6.sin6_addr, caddr, sizeof(struct in6_addr)); memcpy((void *)&sa->sa6.sin6_addr, caddr, sizeof(struct in6_addr));
sa->sa6.sin6_family = AF_INET6; sa->sa6.sin6_family = AF_INET6;
if ((family == java_net_InetAddress_IPv6) && sa->sa6.sin6_scope_id = scopeid;
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;
if (len != NULL) { if (len != NULL) {
*len = sizeof(struct sockaddr_in6); *len = sizeof(struct sockaddr_in6);
} }

View file

@ -604,9 +604,6 @@ java/net/DatagramSocket/SendDatagramToBadAddress.java 7143960 macosx-a
java/net/ServerSocket/AcceptInheritHandle.java 8211854 aix-ppc64 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 # jdk_nio

View file

@ -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");
}
}
}

View file

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

View file

@ -67,7 +67,6 @@ public class B6521014 {
return NetworkConfiguration.probe() return NetworkConfiguration.probe()
.ip6Addresses() .ip6Addresses()
.filter(Inet6Address::isLinkLocalAddress) .filter(Inet6Address::isLinkLocalAddress)
.map(B6521014::removeScope)
.findFirst(); .findFirst();
} }

View file

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