8236184: (dc) IP_MULTICAST_* and IP_TOS socket options not effective
Reviewed-by: dfuchs
This commit is contained in:
parent
75cd193aac
commit
27e0cdf12d
@ -338,23 +338,41 @@ class DatagramChannelImpl
|
||||
|
||||
ProtocolFamily family = familyFor(name);
|
||||
|
||||
// Some platforms require both IPV6_XXX and IP_XXX socket options to
|
||||
// be set when the channel's socket is IPv6 and it is used to send
|
||||
// IPv4 multicast datagrams. The IP_XXX socket options are set on a
|
||||
// best effort basis.
|
||||
boolean needToSetIPv4Option = (family != Net.UNSPEC)
|
||||
&& (this.family == StandardProtocolFamily.INET6)
|
||||
&& Net.shouldSetBothIPv4AndIPv6Options();
|
||||
|
||||
// outgoing multicast interface
|
||||
if (name == StandardSocketOptions.IP_MULTICAST_IF) {
|
||||
NetworkInterface interf = (NetworkInterface)value;
|
||||
assert family != Net.UNSPEC;
|
||||
NetworkInterface interf = (NetworkInterface) value;
|
||||
if (family == StandardProtocolFamily.INET6) {
|
||||
int index = interf.getIndex();
|
||||
if (index == -1)
|
||||
throw new IOException("Network interface cannot be identified");
|
||||
Net.setInterface6(fd, index);
|
||||
} else {
|
||||
}
|
||||
if (family == StandardProtocolFamily.INET || needToSetIPv4Option) {
|
||||
// need IPv4 address to identify interface
|
||||
Inet4Address target = Net.anyInet4Address(interf);
|
||||
if (target == null)
|
||||
if (target != null) {
|
||||
try {
|
||||
Net.setInterface4(fd, Net.inet4AsInt(target));
|
||||
} catch (IOException ioe) {
|
||||
if (family == StandardProtocolFamily.INET) throw ioe;
|
||||
}
|
||||
} else if (family == StandardProtocolFamily.INET) {
|
||||
throw new IOException("Network interface not configured for IPv4");
|
||||
int targetAddress = Net.inet4AsInt(target);
|
||||
Net.setInterface4(fd, targetAddress);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// SO_REUSEADDR needs special handling as it may be emulated
|
||||
if (name == StandardSocketOptions.SO_REUSEADDR
|
||||
&& Net.useExclusiveBind() && localAddress != null) {
|
||||
reuseAddressEmulated = true;
|
||||
@ -363,6 +381,12 @@ class DatagramChannelImpl
|
||||
|
||||
// remaining options don't need any special handling
|
||||
Net.setSocketOption(fd, family, name, value);
|
||||
if (needToSetIPv4Option && family != StandardProtocolFamily.INET) {
|
||||
try {
|
||||
Net.setSocketOption(fd, StandardProtocolFamily.INET, name, value);
|
||||
} catch (IOException ignore) { }
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -106,6 +106,16 @@ public class Net {
|
||||
return exclusiveBind;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether both IPV6_XXX and IP_XXX socket options should be set on
|
||||
* IPv6 sockets. On some kernels, both IPV6_XXX and IP_XXX socket options
|
||||
* need to be set so that the settings are effective for IPv4 multicast
|
||||
* datagrams sent using the socket.
|
||||
*/
|
||||
static boolean shouldSetBothIPv4AndIPv6Options() {
|
||||
return shouldSetBothIPv4AndIPv6Options0();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether IPv6 sockets can join IPv4 multicast groups
|
||||
*/
|
||||
@ -438,6 +448,8 @@ public class Net {
|
||||
*/
|
||||
private static native int isExclusiveBindAvailable();
|
||||
|
||||
private static native boolean shouldSetBothIPv4AndIPv6Options0();
|
||||
|
||||
private static native boolean canIPv6SocketJoinIPv4Group0();
|
||||
|
||||
private static native boolean canJoin6WithIPv4Group0();
|
||||
|
@ -155,6 +155,18 @@ Java_sun_nio_ch_Net_isExclusiveBindAvailable(JNIEnv *env, jclass clazz) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_sun_nio_ch_Net_shouldSetBothIPv4AndIPv6Options0(JNIEnv* env, jclass cl)
|
||||
{
|
||||
#if defined(__linux__)
|
||||
/* Set both IPv4 and IPv6 socket options when setting multicast options */
|
||||
return JNI_TRUE;
|
||||
#else
|
||||
/* Do not set both IPv4 and IPv6 socket options when setting multicast options */
|
||||
return JNI_FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_sun_nio_ch_Net_canIPv6SocketJoinIPv4Group0(JNIEnv* env, jclass cl)
|
||||
{
|
||||
@ -540,12 +552,6 @@ Java_sun_nio_ch_Net_setIntOption0(JNIEnv *env, jclass clazz, jobject fdo,
|
||||
JNU_JAVANETPKG "SocketException",
|
||||
"sun.nio.ch.Net.setIntOption");
|
||||
}
|
||||
#ifdef __linux__
|
||||
if (level == IPPROTO_IPV6 && opt == IPV6_TCLASS && isIPv6) {
|
||||
// set the V4 option also
|
||||
setsockopt(fdval(env, fdo), IPPROTO_IP, IP_TOS, parg, arglen);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
|
@ -25,11 +25,11 @@
|
||||
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
|
||||
#include "jni.h"
|
||||
#include "jni_util.h"
|
||||
#include "jvm.h"
|
||||
#include "jlong.h"
|
||||
|
||||
#include "nio.h"
|
||||
#include "nio_util.h"
|
||||
#include "net_util.h"
|
||||
@ -123,6 +123,12 @@ Java_sun_nio_ch_Net_isExclusiveBindAvailable(JNIEnv *env, jclass clazz) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_sun_nio_ch_Net_shouldSetBothIPv4AndIPv6Options0(JNIEnv* env, jclass cl)
|
||||
{
|
||||
/* Set both IPv4 and IPv6 socket options when setting multicast options */
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_sun_nio_ch_Net_canIPv6SocketJoinIPv4Group0(JNIEnv* env, jclass cl)
|
||||
|
169
test/jdk/java/nio/channels/DatagramChannel/Loopback.java
Normal file
169
test/jdk/java/nio/channels/DatagramChannel/Loopback.java
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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 8236184
|
||||
* @library /test/lib
|
||||
* @build jdk.test.lib.NetworkConfiguration
|
||||
* jdk.test.lib.net.IPSupport
|
||||
* @requires (os.family == "linux") | (os.family == "windows")
|
||||
* @run main Loopback
|
||||
* @run main/othervm -Djava.net.preferIPv4Stack=true Loopback
|
||||
* @summary Test the IP_MULTICAST_LOOP option
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.ProtocolFamily;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.StandardSocketOptions;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.DatagramChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import static java.net.StandardProtocolFamily.*;
|
||||
import static java.net.StandardSocketOptions.IP_MULTICAST_LOOP;
|
||||
|
||||
import jdk.test.lib.NetworkConfiguration;
|
||||
import jdk.test.lib.net.IPSupport;
|
||||
|
||||
public class Loopback {
|
||||
|
||||
static final ProtocolFamily UNSPEC = () -> "UNSPEC";
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
IPSupport.throwSkippedExceptionIfNonOperational();
|
||||
|
||||
// IPv4 and IPv6 interfaces that support multicasting
|
||||
NetworkConfiguration config = NetworkConfiguration.probe();
|
||||
List<NetworkInterface> ip4MulticastInterfaces = config.ip4MulticastInterfaces()
|
||||
.collect(Collectors.toList());
|
||||
List<NetworkInterface> ip6MulticastInterfaces = config.ip6MulticastInterfaces()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// IPv4 multicast group
|
||||
InetAddress ip4Group = InetAddress.getByName("225.4.5.6");
|
||||
for (NetworkInterface ni : ip4MulticastInterfaces) {
|
||||
test(UNSPEC, ip4Group, ni);
|
||||
test(INET, ip4Group, ni);
|
||||
if (IPSupport.hasIPv6()) {
|
||||
test(INET6, ip4Group, ni);
|
||||
}
|
||||
}
|
||||
|
||||
// IPv6 multicast group
|
||||
InetAddress ip6Group = InetAddress.getByName("ff02::a");
|
||||
for (NetworkInterface ni : ip6MulticastInterfaces) {
|
||||
test(UNSPEC, ip6Group, ni);
|
||||
test(INET6, ip6Group, ni);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Joins a multicast group and send datagrams to that group with both
|
||||
* IP_MULTICAST_LOOP enabled and disabled.
|
||||
*/
|
||||
static void test(ProtocolFamily family, InetAddress group, NetworkInterface ni)
|
||||
throws IOException
|
||||
{
|
||||
System.out.format("\n%s socket\n", family.name());
|
||||
DatagramChannel dc;
|
||||
if (family == UNSPEC) {
|
||||
dc = DatagramChannel.open();
|
||||
} else {
|
||||
dc = DatagramChannel.open(family);
|
||||
}
|
||||
try (dc) {
|
||||
dc.setOption(StandardSocketOptions.SO_REUSEADDR, true);
|
||||
dc.bind(new InetSocketAddress(0));
|
||||
int localPort = dc.socket().getLocalPort();
|
||||
SocketAddress target = new InetSocketAddress(group, localPort);
|
||||
|
||||
System.out.format("join %s @ %s%n", group.getHostAddress(), ni.getName());
|
||||
dc.join(group, ni);
|
||||
|
||||
// -- IP_MULTICAST_LOOP enabled --
|
||||
|
||||
assertTrue(dc.getOption(IP_MULTICAST_LOOP), "IP_MULTICAST_LOOP not enabled");
|
||||
System.out.println("IP_MULTICAST_LOOP enabled");
|
||||
|
||||
// send datagram to multicast group
|
||||
System.out.format("send %s -> %s%n", dc.getLocalAddress(), target);
|
||||
ByteBuffer src = ByteBuffer.wrap("hello".getBytes("UTF-8"));
|
||||
dc.send(src, target);
|
||||
|
||||
// receive datagram sent to multicast group
|
||||
ByteBuffer dst = ByteBuffer.allocate(100);
|
||||
int senderPort;
|
||||
do {
|
||||
dst.clear();
|
||||
SocketAddress sender = dc.receive(dst);
|
||||
System.out.format("received %s from %s%n", dst, sender);
|
||||
senderPort = ((InetSocketAddress) sender).getPort();
|
||||
} while (senderPort != localPort);
|
||||
dst.flip();
|
||||
assertTrue(dst.remaining() == src.capacity(), "Unexpected message size");
|
||||
|
||||
// -- IP_MULTICAST_LOOP disabled --
|
||||
|
||||
dc.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, false);
|
||||
System.out.println("IP_MULTICAST_LOOP disabled");
|
||||
|
||||
// send datagram to multicast group
|
||||
System.out.format("send %s -> %s%n", dc.getLocalAddress(), target);
|
||||
src.clear();
|
||||
dc.send(src, target);
|
||||
|
||||
// test that we don't receive the datagram sent to multicast group
|
||||
dc.configureBlocking(false);
|
||||
try (Selector sel = Selector.open()) {
|
||||
dc.register(sel, SelectionKey.OP_READ);
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
int n = sel.select(3000);
|
||||
System.out.format("selected %d%n", n);
|
||||
if (n == 0) {
|
||||
// timeout, no datagram received
|
||||
done = true;
|
||||
} else {
|
||||
sel.selectedKeys().clear();
|
||||
SocketAddress sender = dc.receive(dst);
|
||||
if (sender != null) {
|
||||
System.out.format("received %s from %s%n", dst, sender);
|
||||
senderPort = ((InetSocketAddress) sender).getPort();
|
||||
assertTrue(senderPort != localPort, "Unexpected message");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void assertTrue(boolean e, String msg) {
|
||||
if (!e) throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user