From c99aebff026f9cc81e8956490e8ba629ed72a215 Mon Sep 17 00:00:00 2001 From: coffeys Date: Fri, 12 Jun 2015 12:39:41 +0100 Subject: [PATCH] 8072384: Setting IP_TOS on java.net sockets not working on unix Reviewed-by: michaelm --- make/mapfiles/libnet/mapfile-vers | 4 +- .../java/net/AbstractPlainSocketImpl.java | 15 +++-- .../classes/java/net/DatagramSocket.java | 9 ++- src/share/classes/java/net/Socket.java | 9 ++- .../java/net/PlainDatagramSocketImpl.java | 11 +++- .../classes/java/net/PlainSocketImpl.java | 11 +++- .../native/java/net/PlainDatagramSocketImpl.c | 4 +- src/solaris/native/java/net/PlainSocketImpl.c | 4 +- src/solaris/native/java/net/net_util_md.c | 58 ++++++++----------- test/jdk/net/Sockets/Test.java | 7 ++- 10 files changed, 82 insertions(+), 50 deletions(-) diff --git a/make/mapfiles/libnet/mapfile-vers b/make/mapfiles/libnet/mapfile-vers index 9e07f44b1..12f05a9ce 100644 --- a/make/mapfiles/libnet/mapfile-vers +++ b/make/mapfiles/libnet/mapfile-vers @@ -42,7 +42,7 @@ SUNWprivate_1.1 { 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 @@ SUNWprivate_1.1 { 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; diff --git a/src/share/classes/java/net/AbstractPlainSocketImpl.java b/src/share/classes/java/net/AbstractPlainSocketImpl.java index a3982877d..af7a4a8c3 100644 --- a/src/share/classes/java/net/AbstractPlainSocketImpl.java +++ b/src/share/classes/java/net/AbstractPlainSocketImpl.java @@ -312,11 +312,16 @@ abstract class AbstractPlainSocketImpl extends SocketImpl ret = socketGetOption(opt, null); return new Integer(ret); case IP_TOS: - ret = socketGetOption(opt, null); - if (ret == -1) { // ipv6 tos - return new Integer(trafficClass); - } else { - return new Integer(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); diff --git a/src/share/classes/java/net/DatagramSocket.java b/src/share/classes/java/net/DatagramSocket.java index 2fdabb597..18f2fe1d5 100644 --- a/src/share/classes/java/net/DatagramSocket.java +++ b/src/share/classes/java/net/DatagramSocket.java @@ -1182,7 +1182,14 @@ class DatagramSocket implements java.io.Closeable { if (isClosed()) throw new SocketException("Socket is closed"); - getImpl().setOption(SocketOptions.IP_TOS, new Integer(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; + } } /** diff --git a/src/share/classes/java/net/Socket.java b/src/share/classes/java/net/Socket.java index dedc8ca1b..ff841fc0f 100644 --- a/src/share/classes/java/net/Socket.java +++ b/src/share/classes/java/net/Socket.java @@ -1378,7 +1378,14 @@ class Socket implements java.io.Closeable { if (isClosed()) throw new SocketException("Socket is closed"); - getImpl().setOption(SocketOptions.IP_TOS, new Integer(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; + } } /** diff --git a/src/solaris/classes/java/net/PlainDatagramSocketImpl.java b/src/solaris/classes/java/net/PlainDatagramSocketImpl.java index 1f5eca7fe..2c74dd96f 100644 --- a/src/solaris/classes/java/net/PlainDatagramSocketImpl.java +++ b/src/solaris/classes/java/net/PlainDatagramSocketImpl.java @@ -69,6 +69,15 @@ class PlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl return (T)flow; } + 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; @@ -101,7 +110,7 @@ class PlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl 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; diff --git a/src/solaris/classes/java/net/PlainSocketImpl.java b/src/solaris/classes/java/net/PlainSocketImpl.java index 1f634ea29..7473c1af9 100644 --- a/src/solaris/classes/java/net/PlainSocketImpl.java +++ b/src/solaris/classes/java/net/PlainSocketImpl.java @@ -83,6 +83,15 @@ class PlainSocketImpl extends AbstractPlainSocketImpl return (T)flow; } + 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) @@ -103,7 +112,7 @@ class PlainSocketImpl extends AbstractPlainSocketImpl 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; diff --git a/src/solaris/native/java/net/PlainDatagramSocketImpl.c b/src/solaris/native/java/net/PlainDatagramSocketImpl.c index 0a8a3a4d8..3f1714ec3 100644 --- a/src/solaris/native/java/net/PlainDatagramSocketImpl.c +++ b/src/solaris/native/java/net/PlainDatagramSocketImpl.c @@ -1321,11 +1321,11 @@ static void setMulticastLoopbackMode(JNIEnv *env, jobject this, int fd, /* * 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) { diff --git a/src/solaris/native/java/net/PlainSocketImpl.c b/src/solaris/native/java/net/PlainSocketImpl.c index aef4476ae..724c1cfd8 100644 --- a/src/solaris/native/java/net/PlainSocketImpl.c +++ b/src/solaris/native/java/net/PlainSocketImpl.c @@ -885,11 +885,11 @@ Java_java_net_PlainSocketImpl_socketShutdown(JNIEnv *env, jobject this, /* * 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; diff --git a/src/solaris/native/java/net/net_util_md.c b/src/solaris/native/java/net/net_util_md.c index 769e54050..c6bc4f8cf 100644 --- a/src/solaris/native/java/net/net_util_md.c +++ b/src/solaris/native/java/net/net_util_md.c @@ -1005,12 +1005,10 @@ NET_MapSocketOption(jint cmd, int *level, int *optname) { 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; @@ -1021,6 +1019,13 @@ NET_MapSocketOption(jint cmd, int *level, int *optname) { *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 @@ -1196,9 +1201,6 @@ int getDefaultIPv6Interface(struct in6_addr *target_addr) { * 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. */ @@ -1208,21 +1210,6 @@ NET_GetSockOpt(int fd, int level, int opt, void *result, { int rv; -#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 - #ifdef __solaris__ rv = getsockopt(fd, level, opt, result, len); #else @@ -1273,8 +1260,7 @@ NET_GetSockOpt(int fd, int level, int opt, void *result, * * 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, @@ -1305,9 +1291,9 @@ NET_SetSockOpt(int fd, int level, int opt, const void *arg, /* * 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. @@ -1317,12 +1303,6 @@ NET_SetSockOpt(int fd, int level, int opt, const void *arg, 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; @@ -1330,6 +1310,16 @@ NET_SetSockOpt(int fd, int level, int opt, const void *arg, (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 @@ -1422,7 +1412,7 @@ NET_SetSockOpt(int fd, int level, int opt, const void *arg, * On Linux the receive buffer is used for both socket * structures and the 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) { @@ -1605,7 +1595,7 @@ NET_Bind(int fd, struct sockaddr *him, int len) * 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. */ diff --git a/test/jdk/net/Sockets/Test.java b/test/jdk/net/Sockets/Test.java index 53adafd8d..2f9e735de 100644 --- a/test/jdk/net/Sockets/Test.java +++ b/test/jdk/net/Sockets/Test.java @@ -23,8 +23,9 @@ /* * @test - * @bug 8032808 + * @bug 8032808 8072384 * @run main/othervm -Xcheck:jni Test + * @run main/othervm -Xcheck:jni -Djava.net.preferIPv4Stack=true Test * @run main/othervm/policy=policy.fail -Xcheck:jni Test fail * @run main/othervm/policy=policy.success -Xcheck:jni Test success */ @@ -100,6 +101,10 @@ public class Test { System.out.println ("Set SO_RCVBUF to 5000\ngetting returns: "); System.out.println (Sockets.getOption(s, StandardSocketOptions.SO_RCVBUF)); + Sockets.setOption(ss, StandardSocketOptions.IP_TOS, 128); + System.out.println ("Setting TOS to 128\ngetting returns: "); + System.out.println (Sockets.getOption(ss, StandardSocketOptions.IP_TOS)); + try { Sockets.setOption(ss, StandardSocketOptions.TCP_NODELAY, true); throw new RuntimeException("TCP_NODELAY should not be supported for ServerSocket"); -- GitLab