8290349: IP_DONTFRAGMENT doesn't set DF bit in IPv4 header

Reviewed-by: michaelm, alanb
This commit is contained in:
Daniel Jeliński 2022-08-04 10:52:15 +00:00
parent 26e5c112da
commit ce61eb6ff9
9 changed files with 74 additions and 134 deletions

View File

@ -133,11 +133,11 @@ public abstract class ExtendedSocketOptions {
}
/** Sets the value of a socket option, for the given socket. */
public abstract void setOption(FileDescriptor fd, SocketOption<?> option, Object value)
public abstract void setOption(FileDescriptor fd, SocketOption<?> option, Object value, boolean isIPv6)
throws SocketException;
/** Returns the value of a socket option, for the given socket. */
public abstract Object getOption(FileDescriptor fd, SocketOption<?> option)
public abstract Object getOption(FileDescriptor fd, SocketOption<?> option, boolean isIPv6)
throws SocketException;
protected ExtendedSocketOptions(Set<SocketOption<?>> options) {
@ -206,7 +206,7 @@ public abstract class ExtendedSocketOptions {
}
@Override
public void setOption(FileDescriptor fd, SocketOption<?> option, Object value)
public void setOption(FileDescriptor fd, SocketOption<?> option, Object value, boolean isIPv6)
throws SocketException
{
throw new UnsupportedOperationException(
@ -214,7 +214,7 @@ public abstract class ExtendedSocketOptions {
}
@Override
public Object getOption(FileDescriptor fd, SocketOption<?> option)
public Object getOption(FileDescriptor fd, SocketOption<?> option, boolean isIPv6)
throws SocketException
{
throw new UnsupportedOperationException(

View File

@ -402,9 +402,10 @@ public class Net {
// only simple values supported by this method
Class<?> type = name.type();
boolean isIPv6 = (family == StandardProtocolFamily.INET6);
if (extendedOptions.isOptionSupported(name)) {
extendedOptions.setOption(fd, name, value);
extendedOptions.setOption(fd, name, value, isIPv6);
return;
}
@ -451,7 +452,6 @@ public class Net {
}
boolean mayNeedConversion = (family == UNSPEC);
boolean isIPv6 = (family == StandardProtocolFamily.INET6);
setIntOption0(fd, mayNeedConversion, key.level(), key.name(), arg, isIPv6);
}
@ -467,7 +467,8 @@ public class Net {
Class<?> type = name.type();
if (extendedOptions.isOptionSupported(name)) {
return extendedOptions.getOption(fd, name);
boolean isIPv6 = (family == StandardProtocolFamily.INET6);
return extendedOptions.getOption(fd, name, isIPv6);
}
// only simple values supported by this method

View File

@ -108,13 +108,13 @@ class LinuxSocketOptions extends PlatformSocketOptions {
}
@Override
void setIpDontFragment(int fd, final boolean value) throws SocketException {
setIpDontFragment0(fd, value);
void setIpDontFragment(int fd, final boolean value, boolean isIPv6) throws SocketException {
setIpDontFragment0(fd, value, isIPv6);
}
@Override
boolean getIpDontFragment(int fd) throws SocketException {
return getIpDontFragment0(fd);
boolean getIpDontFragment(int fd, boolean isIPv6) throws SocketException {
return getIpDontFragment0(fd, isIPv6);
}
@Override
@ -130,11 +130,11 @@ class LinuxSocketOptions extends PlatformSocketOptions {
private static native void setTcpkeepAliveProbes0(int fd, int value) throws SocketException;
private static native void setTcpKeepAliveTime0(int fd, int value) throws SocketException;
private static native void setTcpKeepAliveIntvl0(int fd, int value) throws SocketException;
private static native void setIpDontFragment0(int fd, boolean value) throws SocketException;
private static native void setIpDontFragment0(int fd, boolean value, boolean isIPv6) throws SocketException;
private static native int getTcpkeepAliveProbes0(int fd) throws SocketException;
private static native int getTcpKeepAliveTime0(int fd) throws SocketException;
private static native int getTcpKeepAliveIntvl0(int fd) throws SocketException;
private static native boolean getIpDontFragment0(int fd) throws SocketException;
private static native boolean getIpDontFragment0(int fd, boolean isIPv6) throws SocketException;
private static native void setQuickAck0(int fd, boolean on) throws SocketException;
private static native boolean getQuickAck0(int fd) throws SocketException;
private static native long getSoPeerCred0(int fd) throws SocketException;

View File

@ -244,34 +244,18 @@ JNIEXPORT jint JNICALL Java_jdk_net_LinuxSocketOptions_getIncomingNapiId0
return optval;
}
static int socketFamily(jint fd) {
struct sockaddr_storage st;
struct sockaddr *sa = (struct sockaddr *)&st;
socklen_t sa_len = sizeof(st);
if (getsockname(fd, sa, &sa_len) == 0) {
return sa->sa_family;
}
return -1;
}
/*
* Class: jdk_net_LinuxSocketOptions
* Method: setIpDontFragment0
* Signature: (IZ)V
* Signature: (IZZ)V
*/
JNIEXPORT void JNICALL Java_jdk_net_LinuxSocketOptions_setIpDontFragment0
(JNIEnv *env, jobject unused, jint fd, jboolean optval) {
(JNIEnv *env, jobject unused, jint fd, jboolean optval, jboolean isIPv6) {
jint rv, optsetting;
jint family = socketFamily(fd);
if (family == -1) {
handleError(env, family, "get socket family failed");
return;
}
optsetting = optval ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
if (family == AF_INET) {
if (!isIPv6) {
rv = setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &optsetting, sizeof (optsetting));
} else {
rv = setsockopt(fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &optsetting, sizeof (optsetting));
@ -282,18 +266,13 @@ JNIEXPORT void JNICALL Java_jdk_net_LinuxSocketOptions_setIpDontFragment0
/*
* Class: jdk_net_LinuxSocketOptions
* Method: getIpDontFragment0
* Signature: (I)Z;
* Signature: (IZ)Z;
*/
JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_getIpDontFragment0
(JNIEnv *env, jobject unused, jint fd) {
(JNIEnv *env, jobject unused, jint fd, jboolean isIPv6) {
jint optlevel, optname, optval, rv;
jint family = socketFamily(fd);
if (family == -1) {
handleError(env, family, "get socket family failed");
return JNI_FALSE;
}
if (family == AF_INET) {
if (!isIPv6) {
optlevel = IPPROTO_IP;
optname = IP_MTU_DISCOVER;
} else {

View File

@ -84,13 +84,13 @@ class MacOSXSocketOptions extends PlatformSocketOptions {
}
@Override
void setIpDontFragment(int fd, final boolean value) throws SocketException {
setIpDontFragment0(fd, value);
void setIpDontFragment(int fd, final boolean value, boolean isIPv6) throws SocketException {
setIpDontFragment0(fd, value, isIPv6);
}
@Override
boolean getIpDontFragment(int fd) throws SocketException {
return getIpDontFragment0(fd);
boolean getIpDontFragment(int fd, boolean isIPv6) throws SocketException {
return getIpDontFragment0(fd, isIPv6);
}
@Override
@ -106,11 +106,11 @@ class MacOSXSocketOptions extends PlatformSocketOptions {
private static native void setTcpkeepAliveProbes0(int fd, int value) throws SocketException;
private static native void setTcpKeepAliveTime0(int fd, int value) throws SocketException;
private static native void setTcpKeepAliveIntvl0(int fd, int value) throws SocketException;
private static native void setIpDontFragment0(int fd, boolean value) throws SocketException;
private static native void setIpDontFragment0(int fd, boolean value, boolean isIPv6) throws SocketException;
private static native int getTcpkeepAliveProbes0(int fd) throws SocketException;
private static native int getTcpKeepAliveTime0(int fd) throws SocketException;
private static native int getTcpKeepAliveIntvl0(int fd) throws SocketException;
private static native boolean getIpDontFragment0(int fd) throws SocketException;
private static native boolean getIpDontFragment0(int fd, boolean isIPv6) throws SocketException;
private static native long getSoPeerCred0(int fd) throws SocketException;
private static native boolean keepAliveOptionsSupported0();
private static native boolean ipDontFragmentSupported0();

View File

@ -178,17 +178,6 @@ JNIEXPORT jint JNICALL Java_jdk_net_MacOSXSocketOptions_getTcpKeepAliveIntvl0
return optval;
}
static int socketFamily(jint fd) {
struct sockaddr_storage st;
struct sockaddr* sa = (struct sockaddr *)&st;
socklen_t sa_len = sizeof(st);
if (getsockname(fd, sa, &sa_len) == 0) {
return sa->sa_family;
}
return -1;
}
/*
* Class: jdk_net_MacOSXSocketOptions
* Method: ipDontFragmentSupported0
@ -198,42 +187,37 @@ JNIEXPORT jboolean JNICALL Java_jdk_net_MacOSXSocketOptions_ipDontFragmentSuppor
(JNIEnv *env, jobject unused) {
jint rv, fd, value;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1)
return JNI_FALSE;
if (fd != -1) {
value = 1;
rv = setsockopt(fd, IPPROTO_IP, IP_DONTFRAG, &value, sizeof(value));
close(fd);
if (rv == -1) {
return JNI_FALSE;
}
}
fd = socket(AF_INET6, SOCK_DGRAM, 0);
if (fd == -1)
return JNI_FALSE;
if (fd != -1) {
value = 1;
rv = setsockopt(fd, IPPROTO_IPV6, IPV6_DONTFRAG, &value, sizeof(value));
close(fd);
if (rv == -1) {
return JNI_FALSE;
}
}
return JNI_TRUE;
}
/*
* Class: jdk_net_MacOSXSocketOptions
* Method: setIpDontFragment0
* Signature: (IZ)V
* Signature: (IZZ)V
*/
JNIEXPORT void JNICALL Java_jdk_net_MacOSXSocketOptions_setIpDontFragment0
(JNIEnv *env, jobject unused, jint fd, jboolean optval) {
(JNIEnv *env, jobject unused, jint fd, jboolean optval, jboolean isIPv6) {
jint rv;
jint family = socketFamily(fd);
jint value = optval ? 1 : 0;
if (family == -1) {
handleError(env, family, "get socket family failed");
return;
}
if (family == AF_INET) {
if (!isIPv6) {
rv = setsockopt(fd, IPPROTO_IP, IP_DONTFRAG, &value, sizeof(value));
} else {
rv = setsockopt(fd, IPPROTO_IPV6, IPV6_DONTFRAG, &value, sizeof(value));
@ -244,18 +228,13 @@ JNIEXPORT void JNICALL Java_jdk_net_MacOSXSocketOptions_setIpDontFragment0
/*
* Class: jdk_net_MacOSXSocketOptions
* Method: getIpDontFragment0
* Signature: (I)Z;
* Signature: (IZ)Z;
*/
JNIEXPORT jboolean JNICALL Java_jdk_net_MacOSXSocketOptions_getIpDontFragment0
(JNIEnv *env, jobject unused, jint fd) {
(JNIEnv *env, jobject unused, jint fd, jboolean isIPv6) {
jint optval, rv;
socklen_t sz = sizeof (optval);
jint family = socketFamily(fd);
if (family == -1) {
handleError(env, family, "get socket family failed");
return 0;
}
if (family == AF_INET) {
if (!isIPv6) {
rv = getsockopt(fd, IPPROTO_IP, IP_DONTFRAG, &optval, &sz);
} else {
rv = getsockopt(fd, IPPROTO_IPV6, IPV6_DONTFRAG, &optval, &sz);

View File

@ -28,6 +28,7 @@ package jdk.net;
import java.io.FileDescriptor;
import java.net.SocketException;
import java.net.SocketOption;
import java.net.StandardProtocolFamily;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collections;
@ -209,6 +210,9 @@ public final class ExtendedSocketOptions {
* sizes to the {@link java.net.NetworkInterface#getMTU() local MTU}. Depending
* on the implementation and the network interface, packets larger than the MTU
* may be sent or dropped silently or dropped with an exception thrown.
* For {@link StandardProtocolFamily#INET6 IPv6} sockets it is
* system dependent whether the option also applies to datagrams
* sent to IPv4 addresses.
*
* @apiNote
* For IPv4 this option sets the DF (Do not Fragment) flag in the IP packet
@ -263,10 +267,9 @@ public final class ExtendedSocketOptions {
new sun.net.ext.ExtendedSocketOptions(extendedOptions) {
@Override
@SuppressWarnings("removal")
public void setOption(FileDescriptor fd,
SocketOption<?> option,
Object value)
Object value, boolean isIPv6)
throws SocketException
{
if (fd == null || !fd.valid())
@ -277,7 +280,7 @@ public final class ExtendedSocketOptions {
} else if (option == TCP_KEEPCOUNT) {
setTcpkeepAliveProbes(fd, (Integer) value);
} else if (option == IP_DONTFRAGMENT) {
setIpDontFragment(fd, (Boolean) value);
setIpDontFragment(fd, (Boolean) value, isIPv6);
} else if (option == TCP_KEEPIDLE) {
setTcpKeepAliveTime(fd, (Integer) value);
} else if (option == TCP_KEEPINTERVAL) {
@ -295,9 +298,8 @@ public final class ExtendedSocketOptions {
}
@Override
@SuppressWarnings("removal")
public Object getOption(FileDescriptor fd,
SocketOption<?> option)
SocketOption<?> option, boolean isIPv6)
throws SocketException
{
if (fd == null || !fd.valid())
@ -308,7 +310,7 @@ public final class ExtendedSocketOptions {
} else if (option == TCP_KEEPCOUNT) {
return getTcpkeepAliveProbes(fd);
} else if (option == IP_DONTFRAGMENT) {
return getIpDontFragment(fd);
return getIpDontFragment(fd, isIPv6);
} else if (option == TCP_KEEPIDLE) {
return getTcpKeepAliveTime(fd);
} else if (option == TCP_KEEPINTERVAL) {
@ -352,9 +354,9 @@ public final class ExtendedSocketOptions {
platformSocketOptions.setTcpKeepAliveTime(fdAccess.get(fd), value);
}
private static void setIpDontFragment(FileDescriptor fd, boolean value)
private static void setIpDontFragment(FileDescriptor fd, boolean value, boolean isIPv6)
throws SocketException {
platformSocketOptions.setIpDontFragment(fdAccess.get(fd), value);
platformSocketOptions.setIpDontFragment(fdAccess.get(fd), value, isIPv6);
}
private static void setTcpKeepAliveIntvl(FileDescriptor fd, int value)
@ -366,8 +368,8 @@ public final class ExtendedSocketOptions {
return platformSocketOptions.getTcpkeepAliveProbes(fdAccess.get(fd));
}
private static boolean getIpDontFragment(FileDescriptor fd) throws SocketException {
return platformSocketOptions.getIpDontFragment(fdAccess.get(fd));
private static boolean getIpDontFragment(FileDescriptor fd, boolean isIPv6) throws SocketException {
return platformSocketOptions.getIpDontFragment(fdAccess.get(fd), isIPv6);
}
private static int getTcpKeepAliveTime(FileDescriptor fd) throws SocketException {
@ -462,11 +464,11 @@ public final class ExtendedSocketOptions {
throw new UnsupportedOperationException("unsupported TCP_KEEPINTVL option");
}
void setIpDontFragment(int fd, final boolean value) throws SocketException {
void setIpDontFragment(int fd, final boolean value, boolean isIPv6) throws SocketException {
throw new UnsupportedOperationException("unsupported IP_DONTFRAGMENT option");
}
boolean getIpDontFragment(int fd) throws SocketException {
boolean getIpDontFragment(int fd, boolean isIPv6) throws SocketException {
throw new UnsupportedOperationException("unsupported IP_DONTFRAGMENT option");
}

View File

@ -42,17 +42,17 @@ class WindowsSocketOptions extends PlatformSocketOptions {
}
@Override
void setIpDontFragment(int fd, final boolean value) throws SocketException {
setIpDontFragment0(fd, value);
void setIpDontFragment(int fd, final boolean value, boolean isIPv6) throws SocketException {
setIpDontFragment0(fd, value, isIPv6);
}
@Override
boolean getIpDontFragment(int fd) throws SocketException {
return getIpDontFragment0(fd);
boolean getIpDontFragment(int fd, boolean isIPv6) throws SocketException {
return getIpDontFragment0(fd, isIPv6);
}
private static native void setIpDontFragment0(int fd, boolean value) throws SocketException;
private static native boolean getIpDontFragment0(int fd) throws SocketException;
private static native void setIpDontFragment0(int fd, boolean value, boolean isIPv6) throws SocketException;
private static native boolean getIpDontFragment0(int fd, boolean isIPv6) throws SocketException;
static {
if (System.getSecurityManager() == null) {

View File

@ -43,32 +43,16 @@ static void handleError(JNIEnv *env, jint rv, const char *errmsg) {
}
}
static int socketFamily(jint fd) {
WSAPROTOCOL_INFO info;
socklen_t sa_len = sizeof(info);
if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL_INFO, (char *)&info, &sa_len) == 0) {
return info.iAddressFamily;
}
return -1;
}
/*
* Class: jdk_net_WindowsSocketOptions
* Method: setIpDontFragment0
* Signature: (IZ)V
* Signature: (IZZ)V
*/
JNIEXPORT void JNICALL Java_jdk_net_WindowsSocketOptions_setIpDontFragment0
(JNIEnv *env, jobject unused, jint fd, jboolean optval) {
(JNIEnv *env, jobject unused, jint fd, jboolean optval, jboolean isIPv6) {
int rv, opt;
jint family = socketFamily(fd);
if (family == -1) {
handleError(env, family, "get socket family failed");
return;
}
if (family == AF_INET) {
if (!isIPv6) {
opt = optval ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
rv = setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, (char *)&opt, sizeof(int));
if (rv == SOCKET_ERROR && WSAGetLastError() == WSAENOPROTOOPT) {
@ -81,7 +65,7 @@ JNIEXPORT void JNICALL Java_jdk_net_WindowsSocketOptions_setIpDontFragment0
if (rv == SOCKET_ERROR && WSAGetLastError() == WSAENOPROTOOPT) {
/* IPV6_MTU_DISCOVER not supported on W 2016 and older, can use old option */
opt = optval;
rv = setsockopt(fd, IPPROTO_IP, IP_DONTFRAGMENT, (char *)&opt, sizeof(int));
rv = setsockopt(fd, IPPROTO_IPV6, IPV6_DONTFRAG, (char *)&opt, sizeof(int));
}
}
handleError(env, rv, "set option IP_DONTFRAGMENT failed");
@ -90,18 +74,13 @@ JNIEXPORT void JNICALL Java_jdk_net_WindowsSocketOptions_setIpDontFragment0
/*
* Class: jdk_net_WindowsSocketOptions
* Method: getIpDontFragment0
* Signature: (I)Z;
* Signature: (IZ)Z;
*/
JNIEXPORT jboolean JNICALL Java_jdk_net_WindowsSocketOptions_getIpDontFragment0
(JNIEnv *env, jobject unused, jint fd) {
(JNIEnv *env, jobject unused, jint fd, jboolean isIPv6) {
int optval, rv, sz = sizeof(optval);
jint family = socketFamily(fd);
if (family == -1) {
handleError(env, family, "get socket family failed");
return JNI_FALSE;
}
if (family == AF_INET) {
if (!isIPv6) {
rv = getsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, (char *)&optval, &sz);
if (rv == SOCKET_ERROR && WSAGetLastError() == WSAENOPROTOOPT) {
sz = sizeof(optval);
@ -115,7 +94,7 @@ JNIEXPORT jboolean JNICALL Java_jdk_net_WindowsSocketOptions_getIpDontFragment0
rv = getsockopt(fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, (char *)&optval, &sz);
if (rv == SOCKET_ERROR && WSAGetLastError() == WSAENOPROTOOPT) {
sz = sizeof(optval);
rv = getsockopt(fd, IPPROTO_IP, IP_DONTFRAGMENT, (char *)&optval, &sz);
rv = getsockopt(fd, IPPROTO_IPV6, IPV6_DONTFRAG, (char *)&optval, &sz);
handleError(env, rv, "get option IP_DONTFRAGMENT failed");
return optval;
}