8072384: Setting IP_TOS on java.net sockets not working on unix
Reviewed-by: michaelm
--- a/jdk/make/mapfiles/libnet/mapfile-vers Thu Jun 04 10:27:06 2015 +0100
+++ b/jdk/make/mapfiles/libnet/mapfile-vers Thu Jun 04 18:16:25 2015 +0100
@@ -42,7 +42,7 @@
Java_java_net_Inet4Address_init;
Java_java_net_Inet6Address_init;
Java_java_net_PlainDatagramSocketImpl_setTTL;
- Java_java_net_PlainDatagramSocketImpl_socketSetOption;
+ Java_java_net_PlainDatagramSocketImpl_socketSetOption0;
Java_java_net_PlainDatagramSocketImpl_bind0;
Java_java_net_PlainSocketImpl_socketAccept;
Java_java_net_DatagramPacket_init;
@@ -73,7 +73,7 @@
Java_java_net_SocketOutputStream_init;
Java_java_net_PlainDatagramSocketImpl_peek;
Java_java_net_PlainDatagramSocketImpl_peekData;
- Java_java_net_PlainSocketImpl_socketSetOption;
+ Java_java_net_PlainSocketImpl_socketSetOption0;
Java_java_net_PlainSocketImpl_socketSendUrgentData;
Java_java_net_PlainDatagramSocketImpl_datagramSocketCreate;
Java_java_net_PlainSocketImpl_socketGetOption;
--- a/jdk/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java Thu Jun 04 10:27:06 2015 +0100
+++ b/jdk/src/java.base/share/classes/java/net/AbstractPlainSocketImpl.java Thu Jun 04 18:16:25 2015 +0100
@@ -312,11 +312,16 @@
ret = socketGetOption(opt, null);
return ret;
case IP_TOS:
- ret = socketGetOption(opt, null);
- if (ret == -1) { // ipv6 tos
- return trafficClass;
- } else {
- return ret;
+ try {
+ ret = socketGetOption(opt, null);
+ if (ret == -1) { // ipv6 tos
+ return trafficClass;
+ } else {
+ return ret;
+ }
+ } catch (SocketException se) {
+ // TODO - should make better effort to read TOS or TCLASS
+ return trafficClass; // ipv6 tos
}
case SO_KEEPALIVE:
ret = socketGetOption(opt, null);
--- a/jdk/src/java.base/share/classes/java/net/DatagramSocket.java Thu Jun 04 10:27:06 2015 +0100
+++ b/jdk/src/java.base/share/classes/java/net/DatagramSocket.java Thu Jun 04 18:16:25 2015 +0100
@@ -1184,7 +1184,14 @@
if (isClosed())
throw new SocketException("Socket is closed");
- getImpl().setOption(SocketOptions.IP_TOS, tc);
+ try {
+ getImpl().setOption(SocketOptions.IP_TOS, tc);
+ } catch (SocketException se) {
+ // not supported if socket already connected
+ // Solaris returns error in such cases
+ if(!isConnected())
+ throw se;
+ }
}
/**
--- a/jdk/src/java.base/share/classes/java/net/Socket.java Thu Jun 04 10:27:06 2015 +0100
+++ b/jdk/src/java.base/share/classes/java/net/Socket.java Thu Jun 04 18:16:25 2015 +0100
@@ -1380,7 +1380,14 @@
if (isClosed())
throw new SocketException("Socket is closed");
- getImpl().setOption(SocketOptions.IP_TOS, tc);
+ try {
+ getImpl().setOption(SocketOptions.IP_TOS, tc);
+ } catch (SocketException se) {
+ // not supported if socket already connected
+ // Solaris returns error in such cases
+ if(!isConnected())
+ throw se;
+ }
}
/**
--- a/jdk/src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java Thu Jun 04 10:27:06 2015 +0100
+++ b/jdk/src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java Thu Jun 04 18:16:25 2015 +0100
@@ -80,6 +80,15 @@
return options;
}
+ protected void socketSetOption(int opt, Object val) throws SocketException {
+ try {
+ socketSetOption0(opt, val);
+ } catch (SocketException se) {
+ if (!connected)
+ throw se;
+ }
+ }
+
protected synchronized native void bind0(int lport, InetAddress laddr)
throws SocketException;
@@ -112,7 +121,7 @@
protected native void datagramSocketClose();
- protected native void socketSetOption(int opt, Object val)
+ protected native void socketSetOption0(int opt, Object val)
throws SocketException;
protected native Object socketGetOption(int opt) throws SocketException;
--- a/jdk/src/java.base/unix/classes/java/net/PlainSocketImpl.java Thu Jun 04 10:27:06 2015 +0100
+++ b/jdk/src/java.base/unix/classes/java/net/PlainSocketImpl.java Thu Jun 04 18:16:25 2015 +0100
@@ -94,6 +94,15 @@
return options;
}
+ protected void socketSetOption(int opt, boolean b, Object val) throws SocketException {
+ try {
+ socketSetOption0(opt, b, val);
+ } catch (SocketException se) {
+ if (socket == null || !socket.isConnected())
+ throw se;
+ }
+ }
+
native void socketCreate(boolean isServer) throws IOException;
native void socketConnect(InetAddress address, int port, int timeout)
@@ -114,7 +123,7 @@
static native void initProto();
- native void socketSetOption(int cmd, boolean on, Object value)
+ native void socketSetOption0(int cmd, boolean on, Object value)
throws SocketException;
native int socketGetOption(int opt, Object iaContainerObj) throws SocketException;
--- a/jdk/src/java.base/unix/native/libnet/PlainDatagramSocketImpl.c Thu Jun 04 10:27:06 2015 +0100
+++ b/jdk/src/java.base/unix/native/libnet/PlainDatagramSocketImpl.c Thu Jun 04 18:16:25 2015 +0100
@@ -1294,11 +1294,11 @@
/*
* Class: java_net_PlainDatagramSocketImpl
- * Method: socketSetOption
+ * Method: socketSetOption0
* Signature: (ILjava/lang/Object;)V
*/
JNIEXPORT void JNICALL
-Java_java_net_PlainDatagramSocketImpl_socketSetOption(JNIEnv *env,
+Java_java_net_PlainDatagramSocketImpl_socketSetOption0(JNIEnv *env,
jobject this,
jint opt,
jobject value) {
--- a/jdk/src/java.base/unix/native/libnet/PlainSocketImpl.c Thu Jun 04 10:27:06 2015 +0100
+++ b/jdk/src/java.base/unix/native/libnet/PlainSocketImpl.c Thu Jun 04 18:16:25 2015 +0100
@@ -847,11 +847,11 @@
/*
* Class: java_net_PlainSocketImpl
- * Method: socketSetOption
+ * Method: socketSetOption0
* Signature: (IZLjava/lang/Object;)V
*/
JNIEXPORT void JNICALL
-Java_java_net_PlainSocketImpl_socketSetOption(JNIEnv *env, jobject this,
+Java_java_net_PlainSocketImpl_socketSetOption0(JNIEnv *env, jobject this,
jint cmd, jboolean on,
jobject value) {
int fd;
--- a/jdk/src/java.base/unix/native/libnet/net_util_md.c Thu Jun 04 10:27:06 2015 +0100
+++ b/jdk/src/java.base/unix/native/libnet/net_util_md.c Thu Jun 04 18:16:25 2015 +0100
@@ -1023,12 +1023,10 @@
int i;
- /*
- * Different multicast options if IPv6 is enabled
- */
#ifdef AF_INET6
if (ipv6_available()) {
switch (cmd) {
+ // Different multicast options if IPv6 is enabled
case java_net_SocketOptions_IP_MULTICAST_IF:
case java_net_SocketOptions_IP_MULTICAST_IF2:
*level = IPPROTO_IPV6;
@@ -1039,6 +1037,13 @@
*level = IPPROTO_IPV6;
*optname = IPV6_MULTICAST_LOOP;
return 0;
+#if (defined(__solaris__) || defined(MACOSX))
+ // Map IP_TOS request to IPV6_TCLASS
+ case java_net_SocketOptions_IP_TOS:
+ *level = IPPROTO_IPV6;
+ *optname = IPV6_TCLASS;
+ return 0;
+#endif
}
}
#endif
@@ -1214,9 +1219,6 @@
* Wrapper for getsockopt system routine - does any necessary
* pre/post processing to deal with OS specific oddities :-
*
- * IP_TOS is a no-op with IPv6 sockets as it's setup when
- * the connection is established.
- *
* On Linux the SO_SNDBUF/SO_RCVBUF values must be post-processed
* to compensate for an incorrect value returned by the kernel.
*/
@@ -1227,21 +1229,6 @@
int rv;
socklen_t socklen = *len;
-#ifdef AF_INET6
- if ((level == IPPROTO_IP) && (opt == IP_TOS)) {
- if (ipv6_available()) {
-
- /*
- * For IPv6 socket option implemented at Java-level
- * so return -1.
- */
- int *tc = (int *)result;
- *tc = -1;
- return 0;
- }
- }
-#endif
-
rv = getsockopt(fd, level, opt, result, &socklen);
*len = socklen;
@@ -1285,8 +1272,7 @@
*
* For IP_TOS socket option need to mask off bits as this
* aren't automatically masked by the kernel and results in
- * an error. In addition IP_TOS is a NOOP with IPv6 as it
- * should be setup as connection time.
+ * an error.
*/
int
NET_SetSockOpt(int fd, int level, int opt, const void *arg,
@@ -1317,9 +1303,9 @@
/*
* IPPROTO/IP_TOS :-
- * 1. IPv6 on Solaris/Mac OS: NOOP and will be set
- * in flowinfo field when connecting TCP socket,
- * or sending UDP packet.
+ * 1. IPv6 on Solaris/Mac OS:
+ * Set the TOS OR Traffic Class value to cater for
+ * IPv6 and IPv4 scenarios.
* 2. IPv6 on Linux: By default Linux ignores flowinfo
* field so enable IPV6_FLOWINFO_SEND so that flowinfo
* will be examined. We also set the IPv4 TOS option in this case.
@@ -1329,12 +1315,6 @@
if (level == IPPROTO_IP && opt == IP_TOS) {
int *iptos;
-#if defined(AF_INET6) && (defined(__solaris__) || defined(MACOSX))
- if (ipv6_available()) {
- return 0;
- }
-#endif
-
#if defined(AF_INET6) && defined(__linux__)
if (ipv6_available()) {
int optval = 1;
@@ -1342,6 +1322,16 @@
(void *)&optval, sizeof(optval)) < 0) {
return -1;
}
+ /*
+ * Let's also set the IPV6_TCLASS flag.
+ * Linux appears to allow both IP_TOS and IPV6_TCLASS to be set
+ * This helps in mixed environments where IPv4 and IPv6 sockets
+ * are connecting.
+ */
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS,
+ arg, len) < 0) {
+ return -1;
+ }
}
#endif
@@ -1435,7 +1425,7 @@
* On Linux the receive buffer is used for both socket
* structures and the packet payload. The implication
* is that if SO_RCVBUF is too small then small packets
- * must be discard.
+ * must be discarded.
*/
#ifdef __linux__
if (level == SOL_SOCKET && opt == SO_RCVBUF) {
@@ -1619,7 +1609,7 @@
* NET_WAIT_READ, NET_WAIT_WRITE & NET_WAIT_CONNECT.
*
* The function will return when either the socket is ready for one
- * of the specified operation or the timeout expired.
+ * of the specified operations or the timeout expired.
*
* It returns the time left from the timeout (possibly 0), or -1 if it expired.
*/
--- a/jdk/test/java/net/SocketOption/OptionsTest.java Thu Jun 04 10:27:06 2015 +0100
+++ b/jdk/test/java/net/SocketOption/OptionsTest.java Thu Jun 04 18:16:25 2015 +0100
@@ -23,8 +23,9 @@
/*
* @test
- * @bug 8036979
+ * @bug 8036979 8072384
* @run main/othervm -Xcheck:jni OptionsTest
+ * @run main/othervm -Xcheck:jni -Djava.net.preferIPv4Stack=true OptionsTest
*/
import java.net.*;
@@ -59,7 +60,8 @@
static Test[] serverSocketTests = new Test[] {
Test.create(StandardSocketOptions.SO_RCVBUF, Integer.valueOf(8 * 100)),
- Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE)
+ Test.create(StandardSocketOptions.SO_REUSEADDR, Boolean.FALSE),
+ Test.create(StandardSocketOptions.IP_TOS, Integer.valueOf(100))
};
static Test[] dgSocketTests = new Test[] {
@@ -193,6 +195,9 @@
return Integer.valueOf(socket.getReceiveBufferSize());
} else if (option.equals(StandardSocketOptions.SO_REUSEADDR)) {
return Boolean.valueOf(socket.getReuseAddress());
+ } else if (option.equals(StandardSocketOptions.IP_TOS)) {
+ return Integer.valueOf(jdk.net.Sockets.getOption(
+ socket, StandardSocketOptions.IP_TOS));
} else {
throw new RuntimeException("unexecpted socket option");
}