mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8216417: cleanup of IPv6 scope-id handling
Reviewed-by: alanb, chegar, dfuchs
This commit is contained in:
parent
04ea6ae9b3
commit
247a6a2ce4
23 changed files with 761 additions and 595 deletions
|
@ -33,6 +33,7 @@ import java.util.Set;
|
|||
|
||||
import sun.net.ResourceManager;
|
||||
import sun.net.ext.ExtendedSocketOptions;
|
||||
import sun.net.util.IPAddressUtil;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
/**
|
||||
|
@ -110,6 +111,9 @@ abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
|
|||
*/
|
||||
protected synchronized void bind(int lport, InetAddress laddr)
|
||||
throws SocketException {
|
||||
if (laddr.isLinkLocalAddress()) {
|
||||
laddr = IPAddressUtil.toScopedAddress(laddr);
|
||||
}
|
||||
bind0(lport, laddr);
|
||||
}
|
||||
|
||||
|
@ -121,7 +125,19 @@ abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
|
|||
* 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 @@ abstract class AbstractPlainDatagramSocketImpl extends DatagramSocketImpl
|
|||
* @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;
|
||||
|
|
|
@ -43,6 +43,7 @@ import sun.net.NetHooks;
|
|||
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 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
|
|||
boolean connected = false;
|
||||
try {
|
||||
InetAddress address = InetAddress.getByName(host);
|
||||
this.port = port;
|
||||
// recording this.address as supplied by caller before calling connect
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
if (address.isLinkLocalAddress()) {
|
||||
address = IPAddressUtil.toScopedAddress(address);
|
||||
}
|
||||
|
||||
connectToAddress(address, port, timeout);
|
||||
connected = true;
|
||||
|
@ -182,8 +187,12 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
|
|||
* @param port the specified port
|
||||
*/
|
||||
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.port = port;
|
||||
if (address.isLinkLocalAddress()) {
|
||||
address = IPAddressUtil.toScopedAddress(address);
|
||||
}
|
||||
|
||||
try {
|
||||
connectToAddress(address, port, timeout);
|
||||
|
@ -215,10 +224,14 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
|
|||
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 @@ abstract class AbstractPlainSocketImpl extends SocketImpl implements PlatformSoc
|
|||
NetHooks.beforeTcpBind(fd, address, lport);
|
||||
}
|
||||
}
|
||||
if (address.isLinkLocalAddress()) {
|
||||
address = IPAddressUtil.toScopedAddress(address);
|
||||
}
|
||||
socketBind(address, lport);
|
||||
isBound = true;
|
||||
}
|
||||
|
|
|
@ -176,11 +176,6 @@ public final
|
|||
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() {
|
||||
|
|
|
@ -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 @@ public class IPAddressUtil {
|
|||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||
|
||||
import sun.net.ResourceManager;
|
||||
import sun.net.ext.ExtendedSocketOptions;
|
||||
import sun.net.util.IPAddressUtil;
|
||||
|
||||
/**
|
||||
* An implementation of DatagramChannels.
|
||||
|
@ -527,14 +528,16 @@ class DatagramChannelImpl
|
|||
} 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()) {
|
||||
|
|
|
@ -50,6 +50,7 @@ import java.security.PrivilegedAction;
|
|||
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 @@ public class Net {
|
|||
{
|
||||
boolean preferIPv6 = isIPv6Available() &&
|
||||
(family != StandardProtocolFamily.INET);
|
||||
if (addr.isLinkLocalAddress()) {
|
||||
addr = IPAddressUtil.toScopedAddress(addr);
|
||||
}
|
||||
bind0(fd, preferIPv6, exclusiveBind, addr, port);
|
||||
}
|
||||
|
||||
|
@ -481,6 +485,9 @@ public class Net {
|
|||
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);
|
||||
|
|
|
@ -37,7 +37,6 @@ jfieldID ia6_holder6ID;
|
|||
|
||||
jfieldID ia6_ipaddressID;
|
||||
jfieldID ia6_scopeidID;
|
||||
jfieldID ia6_cachedscopeidID;
|
||||
jfieldID ia6_scopeidsetID;
|
||||
jfieldID ia6_scopeifnameID;
|
||||
jmethodID ia6_ctrID;
|
||||
|
@ -65,8 +64,6 @@ Java_java_net_Inet6Address_init(JNIEnv *env, jclass cls) {
|
|||
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;");
|
||||
|
|
|
@ -107,7 +107,6 @@ extern jclass ia6_class;
|
|||
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;
|
||||
|
|
|
@ -57,7 +57,7 @@ class PlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl
|
|||
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;
|
||||
|
||||
|
|
|
@ -703,10 +703,6 @@ Java_java_net_Inet6AddressImpl_isReachable0(JNIEnv *env, jobject this,
|
|||
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
|
||||
|
|
|
@ -79,10 +79,6 @@ static jfieldID pdsi_connected;
|
|||
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 @@ Java_java_net_PlainDatagramSocketImpl_bind0(JNIEnv *env, jobject this,
|
|||
JNI_TRUE) != 0) {
|
||||
return;
|
||||
}
|
||||
setDefaultScopeID(env, &sa.sa);
|
||||
|
||||
if (NET_Bind(fd, &sa, len) < 0) {
|
||||
if (errno == EADDRINUSE || errno == EADDRNOTAVAIL ||
|
||||
|
@ -266,8 +261,6 @@ Java_java_net_PlainDatagramSocketImpl_connect0(JNIEnv *env, jobject this,
|
|||
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 @@ Java_java_net_PlainDatagramSocketImpl_disconnect0(JNIEnv *env, jobject this, jin
|
|||
|
||||
/*
|
||||
* 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 @@ Java_java_net_PlainDatagramSocketImpl_send(JNIEnv *env, jobject this,
|
|||
}
|
||||
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 @@ static void mcast_join_leave(JNIEnv *env, jobject this,
|
|||
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);
|
||||
|
|
|
@ -46,8 +46,6 @@ jfieldID psi_trafficClassID;
|
|||
jfieldID psi_fdLockID;
|
||||
jfieldID psi_closePendingID;
|
||||
|
||||
extern void setDefaultScopeID(JNIEnv *env, struct sockaddr *him);
|
||||
|
||||
/*
|
||||
* file descriptor used for dup2
|
||||
*/
|
||||
|
@ -261,7 +259,6 @@ Java_java_net_PlainSocketImpl_socketConnect(JNIEnv *env, jobject this,
|
|||
JNI_TRUE) != 0) {
|
||||
return;
|
||||
}
|
||||
setDefaultScopeID(env, &sa.sa);
|
||||
|
||||
if (trafficClass != 0 && ipv6_available()) {
|
||||
NET_SetTrafficClass(&sa, trafficClass);
|
||||
|
@ -509,7 +506,6 @@ Java_java_net_PlainSocketImpl_socketBind(JNIEnv *env, jobject this,
|
|||
&len, JNI_TRUE) != 0) {
|
||||
return;
|
||||
}
|
||||
setDefaultScopeID(env, &sa.sa);
|
||||
|
||||
if (NET_Bind(fd, &sa, len) < 0) {
|
||||
if (errno == EADDRINUSE || errno == EADDRNOTAVAIL ||
|
||||
|
|
|
@ -452,285 +452,7 @@ void NET_ThrowUnknownHostExceptionWithGaiError(JNIEnv *env,
|
|||
}
|
||||
}
|
||||
|
||||
#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 @@ NET_InetAddressToSockaddr(JNIEnv *env, jobject iaObj, int port,
|
|||
*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) {
|
||||
|
@ -1014,158 +677,6 @@ NET_MapSocketOption(jint cmd, int *level, int *optname) {
|
|||
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
|
||||
* pre/post processing to deal with OS specific oddities :-
|
||||
|
|
|
@ -101,7 +101,6 @@ void NET_SetTrafficClass(SOCKETADDRESS *sa, int trafficClass);
|
|||
|
||||
#ifdef __linux__
|
||||
int kernelIsV24();
|
||||
int getDefaultIPv6Interface(struct in6_addr *target_addr);
|
||||
#endif
|
||||
|
||||
#ifdef __solaris__
|
||||
|
|
|
@ -124,7 +124,7 @@ class DualStackPlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl
|
|||
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)
|
||||
|
|
|
@ -184,7 +184,7 @@ final class TwoStacksPlainDatagramSocketImpl extends AbstractPlainDatagramSocket
|
|||
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;
|
||||
|
||||
|
|
|
@ -415,11 +415,11 @@ Java_java_net_TwoStacksPlainDatagramSocketImpl_disconnect0(JNIEnv *env, jobject
|
|||
|
||||
/*
|
||||
* 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];
|
||||
|
|
|
@ -752,37 +752,6 @@ NET_BindV6(struct ipv6bind *b, jboolean exclBind) {
|
|||
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 @@ NET_InetAddressToSockaddr(JNIEnv *env, jobject iaObj, int port,
|
|||
{
|
||||
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 @@ NET_InetAddressToSockaddr(JNIEnv *env, jobject iaObj, int port,
|
|||
} 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);
|
||||
}
|
||||
|
|
|
@ -604,9 +604,6 @@ java/net/DatagramSocket/SendDatagramToBadAddress.java 7143960 macosx-a
|
|||
|
||||
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
|
||||
|
|
196
test/jdk/java/net/Inet6Address/Scoping.java
Normal file
196
test/jdk/java/net/Inet6Address/Scoping.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
185
test/jdk/java/net/MulticastSocket/PromiscuousIPv6.java
Normal file
185
test/jdk/java/net/MulticastSocket/PromiscuousIPv6.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -67,7 +67,6 @@ public class B6521014 {
|
|||
return NetworkConfiguration.probe()
|
||||
.ip6Addresses()
|
||||
.filter(Inet6Address::isLinkLocalAddress)
|
||||
.map(B6521014::removeScope)
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
|
|
233
test/jdk/java/nio/channels/DatagramChannel/PromiscuousIPv6.java
Normal file
233
test/jdk/java/nio/channels/DatagramChannel/PromiscuousIPv6.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue