8072384: Setting IP_TOS on java.net sockets not working on unix

Reviewed-by: michaelm
This commit is contained in:
Sean Coffey 2015-06-04 18:16:25 +01:00
parent 46b53cd5cf
commit 4ae5f74173
10 changed files with 83 additions and 51 deletions

View File

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

View File

@ -312,11 +312,16 @@ abstract class AbstractPlainSocketImpl extends SocketImpl
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);

View File

@ -1184,7 +1184,14 @@ class DatagramSocket implements java.io.Closeable {
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;
}
}
/**

View File

@ -1380,7 +1380,14 @@ class Socket implements java.io.Closeable {
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;
}
}
/**

View File

@ -80,6 +80,15 @@ class PlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl
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 @@ 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;

View File

@ -94,6 +94,15 @@ class PlainSocketImpl extends AbstractPlainSocketImpl
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 @@ 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;

View File

@ -1294,11 +1294,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) {

View File

@ -847,11 +847,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;

View File

@ -1023,12 +1023,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;
@ -1039,6 +1037,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
@ -1214,9 +1219,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.
*/
@ -1227,21 +1229,6 @@ NET_GetSockOpt(int fd, int level, int opt, void *result,
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 @@ 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,
@ -1317,9 +1303,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.
@ -1329,12 +1315,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;
@ -1342,6 +1322,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
@ -1435,7 +1425,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 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_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.
*/

View File

@ -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 @@ public class OptionsTest {
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 @@ public class OptionsTest {
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");
}