mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 23:34:52 +02:00
8284890: Support for Do not fragment IP socket options
Reviewed-by: erikj, ihse, dfuchs
This commit is contained in:
parent
a7b5157375
commit
67755edd6f
11 changed files with 498 additions and 3 deletions
|
@ -27,7 +27,7 @@ include LibCommon.gmk
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
ifeq ($(call isTargetOs, linux macosx), true)
|
ifeq ($(call isTargetOs, linux macosx windows), true)
|
||||||
|
|
||||||
$(eval $(call SetupJdkLibrary, BUILD_LIBEXTNET, \
|
$(eval $(call SetupJdkLibrary, BUILD_LIBEXTNET, \
|
||||||
NAME := extnet, \
|
NAME := extnet, \
|
||||||
|
@ -35,8 +35,9 @@ ifeq ($(call isTargetOs, linux macosx), true)
|
||||||
CFLAGS := $(CFLAGS_JDKLIB), \
|
CFLAGS := $(CFLAGS_JDKLIB), \
|
||||||
LDFLAGS := $(LDFLAGS_JDKLIB) \
|
LDFLAGS := $(LDFLAGS_JDKLIB) \
|
||||||
$(call SET_SHARED_LIBRARY_ORIGIN), \
|
$(call SET_SHARED_LIBRARY_ORIGIN), \
|
||||||
LIBS := -ljava, \
|
LIBS_unix := -ljava, \
|
||||||
LIBS_linux := -ljvm, \
|
LIBS_linux := -ljvm, \
|
||||||
|
LIBS_windows := jvm.lib ws2_32.lib $(WIN_JAVA_LIB), \
|
||||||
))
|
))
|
||||||
|
|
||||||
$(BUILD_LIBEXTNET): $(call FindLib, java.base, java)
|
$(BUILD_LIBEXTNET): $(call FindLib, java.base, java)
|
||||||
|
|
|
@ -108,7 +108,8 @@ public abstract class ExtendedSocketOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isStreamOption(SocketOption<?> option, boolean server) {
|
private static boolean isStreamOption(SocketOption<?> option, boolean server) {
|
||||||
if (option.name().startsWith("UDP_") || isUnixDomainOption(option)) {
|
if (option.name().startsWith("UDP_") || isUnixDomainOption(option)
|
||||||
|
|| option.name().equals("IP_DONTFRAGMENT")) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -102,6 +102,16 @@ class LinuxSocketOptions extends PlatformSocketOptions {
|
||||||
return getIncomingNapiId0(fd);
|
return getIncomingNapiId0(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void setIpDontFragment(int fd, final boolean value) throws SocketException {
|
||||||
|
setIpDontFragment0(fd, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean getIpDontFragment(int fd) throws SocketException {
|
||||||
|
return getIpDontFragment0(fd);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
UnixDomainPrincipal getSoPeerCred(int fd) throws SocketException {
|
UnixDomainPrincipal getSoPeerCred(int fd) throws SocketException {
|
||||||
long l = getSoPeerCred0(fd);
|
long l = getSoPeerCred0(fd);
|
||||||
|
@ -115,9 +125,11 @@ class LinuxSocketOptions extends PlatformSocketOptions {
|
||||||
private static native void setTcpkeepAliveProbes0(int fd, int value) throws SocketException;
|
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 setTcpKeepAliveTime0(int fd, int value) throws SocketException;
|
||||||
private static native void setTcpKeepAliveIntvl0(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 int getTcpkeepAliveProbes0(int fd) throws SocketException;
|
private static native int getTcpkeepAliveProbes0(int fd) throws SocketException;
|
||||||
private static native int getTcpKeepAliveTime0(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 int getTcpKeepAliveIntvl0(int fd) throws SocketException;
|
||||||
|
private static native boolean getIpDontFragment0(int fd) throws SocketException;
|
||||||
private static native void setQuickAck0(int fd, boolean on) 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 boolean getQuickAck0(int fd) throws SocketException;
|
||||||
private static native long getSoPeerCred0(int fd) throws SocketException;
|
private static native long getSoPeerCred0(int fd) throws SocketException;
|
||||||
|
|
|
@ -243,3 +243,65 @@ JNIEXPORT jint JNICALL Java_jdk_net_LinuxSocketOptions_getIncomingNapiId0
|
||||||
handleError(env, rv, "get option SO_INCOMING_NAPI_ID failed");
|
handleError(env, rv, "get option SO_INCOMING_NAPI_ID failed");
|
||||||
return optval;
|
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
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_jdk_net_LinuxSocketOptions_setIpDontFragment0
|
||||||
|
(JNIEnv *env, jobject unused, jint fd, jboolean optval) {
|
||||||
|
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) {
|
||||||
|
rv = setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &optsetting, sizeof (optsetting));
|
||||||
|
} else {
|
||||||
|
rv = setsockopt(fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &optsetting, sizeof (optsetting));
|
||||||
|
}
|
||||||
|
handleError(env, rv, "set option IP_DONTFRAGMENT failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: jdk_net_LinuxSocketOptions
|
||||||
|
* Method: getIpDontFragment0
|
||||||
|
* Signature: (I)Z;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_getIpDontFragment0
|
||||||
|
(JNIEnv *env, jobject unused, jint fd) {
|
||||||
|
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) {
|
||||||
|
optlevel = IPPROTO_IP;
|
||||||
|
optname = IP_MTU_DISCOVER;
|
||||||
|
} else {
|
||||||
|
optlevel = IPPROTO_IPV6;
|
||||||
|
optname = IPV6_MTU_DISCOVER;
|
||||||
|
}
|
||||||
|
socklen_t sz = sizeof(optval);
|
||||||
|
rv = getsockopt(fd, optlevel, optname, &optval, &sz);
|
||||||
|
handleError(env, rv, "get option IP_DONTFRAGMENT failed");
|
||||||
|
return optval == IP_PMTUDISC_DO ? JNI_TRUE : JNI_FALSE;
|
||||||
|
}
|
||||||
|
|
|
@ -78,6 +78,16 @@ class MacOSXSocketOptions extends PlatformSocketOptions {
|
||||||
return getTcpKeepAliveIntvl0(fd);
|
return getTcpKeepAliveIntvl0(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void setIpDontFragment(int fd, final boolean value) throws SocketException {
|
||||||
|
setIpDontFragment0(fd, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean getIpDontFragment(int fd) throws SocketException {
|
||||||
|
return getIpDontFragment0(fd);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
UnixDomainPrincipal getSoPeerCred(int fd) throws SocketException {
|
UnixDomainPrincipal getSoPeerCred(int fd) throws SocketException {
|
||||||
long l = getSoPeerCred0(fd);
|
long l = getSoPeerCred0(fd);
|
||||||
|
@ -91,9 +101,11 @@ class MacOSXSocketOptions extends PlatformSocketOptions {
|
||||||
private static native void setTcpkeepAliveProbes0(int fd, int value) throws SocketException;
|
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 setTcpKeepAliveTime0(int fd, int value) throws SocketException;
|
||||||
private static native void setTcpKeepAliveIntvl0(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 int getTcpkeepAliveProbes0(int fd) throws SocketException;
|
private static native int getTcpkeepAliveProbes0(int fd) throws SocketException;
|
||||||
private static native int getTcpKeepAliveTime0(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 int getTcpKeepAliveIntvl0(int fd) throws SocketException;
|
||||||
|
private static native boolean getIpDontFragment0(int fd) throws SocketException;
|
||||||
private static native long getSoPeerCred0(int fd) throws SocketException;
|
private static native long getSoPeerCred0(int fd) throws SocketException;
|
||||||
private static native boolean keepAliveOptionsSupported0();
|
private static native boolean keepAliveOptionsSupported0();
|
||||||
static {
|
static {
|
||||||
|
|
|
@ -30,7 +30,14 @@
|
||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
|
|
||||||
|
#define __APPLE_USE_RFC_3542
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#ifndef IP_DONTFRAG
|
||||||
|
#define IP_DONTFRAG 28
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "jni_util.h"
|
#include "jni_util.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -170,3 +177,60 @@ JNIEXPORT jint JNICALL Java_jdk_net_MacOSXSocketOptions_getTcpKeepAliveIntvl0
|
||||||
handleError(env, rv, "get option TCP_KEEPINTVL failed");
|
handleError(env, rv, "get option TCP_KEEPINTVL failed");
|
||||||
return optval;
|
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: setIpDontFragment0
|
||||||
|
* Signature: (IZ)V
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_jdk_net_MacOSXSocketOptions_setIpDontFragment0
|
||||||
|
(JNIEnv *env, jobject unused, jint fd, jboolean optval) {
|
||||||
|
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) {
|
||||||
|
rv = setsockopt(fd, IPPROTO_IP, IP_DONTFRAG, &value, sizeof(value));
|
||||||
|
} else {
|
||||||
|
rv = setsockopt(fd, IPPROTO_IPV6, IPV6_DONTFRAG, &value, sizeof(value));
|
||||||
|
}
|
||||||
|
handleError(env, rv, "set option IP_DONTFRAGMENT failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: jdk_net_MacOSXSocketOptions
|
||||||
|
* Method: getIpDontFragment0
|
||||||
|
* Signature: (I)Z;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jboolean JNICALL Java_jdk_net_MacOSXSocketOptions_getIpDontFragment0
|
||||||
|
(JNIEnv *env, jobject unused, jint fd) {
|
||||||
|
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) {
|
||||||
|
rv = getsockopt(fd, IPPROTO_IP, IP_DONTFRAG, &optval, &sz);
|
||||||
|
} else {
|
||||||
|
rv = getsockopt(fd, IPPROTO_IPV6, IPV6_DONTFRAG, &optval, &sz);
|
||||||
|
}
|
||||||
|
handleError(env, rv, "get option IP_DONTFRAGMENT failed");
|
||||||
|
return optval;
|
||||||
|
}
|
||||||
|
|
|
@ -199,6 +199,28 @@ public final class ExtendedSocketOptions {
|
||||||
= new ExtSocketOption<UnixDomainPrincipal>
|
= new ExtSocketOption<UnixDomainPrincipal>
|
||||||
("SO_PEERCRED", UnixDomainPrincipal.class);
|
("SO_PEERCRED", UnixDomainPrincipal.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable IP packet fragmentation.
|
||||||
|
*
|
||||||
|
* <p> The value of this socket option is a {@code Boolean} that represents
|
||||||
|
* whether the option is enabled or disabled. When {@code true} fragmentation
|
||||||
|
* of outgoing IPv4 and IPv6 packets does not occur. This option can only be used
|
||||||
|
* with datagram sockets. When set, care must be taken to limit outgoing packet
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @apiNote
|
||||||
|
* For IPv4 this option sets the DF (Do not Fragment) flag in the IP packet
|
||||||
|
* header. This instructs intermediate routers to not fragment the packet.
|
||||||
|
* IPv6 routers never fragment packets. Instead, fragmentation is handled
|
||||||
|
* by the sending and receiving nodes exclusively. Setting this option for
|
||||||
|
* an IPv6 socket ensures that packets to be sent are never fragmented, in
|
||||||
|
* which case, the local network MTU must be observed.
|
||||||
|
*/
|
||||||
|
public static final SocketOption<Boolean> IP_DONTFRAGMENT =
|
||||||
|
new ExtSocketOption<Boolean>("IP_DONTFRAGMENT", Boolean.class);
|
||||||
|
|
||||||
private static final PlatformSocketOptions platformSocketOptions =
|
private static final PlatformSocketOptions platformSocketOptions =
|
||||||
PlatformSocketOptions.get();
|
PlatformSocketOptions.get();
|
||||||
|
|
||||||
|
@ -210,6 +232,7 @@ public final class ExtendedSocketOptions {
|
||||||
platformSocketOptions.peerCredentialsSupported();
|
platformSocketOptions.peerCredentialsSupported();
|
||||||
private static final boolean incomingNapiIdOptSupported =
|
private static final boolean incomingNapiIdOptSupported =
|
||||||
platformSocketOptions.incomingNapiIdSupported();
|
platformSocketOptions.incomingNapiIdSupported();
|
||||||
|
|
||||||
private static final Set<SocketOption<?>> extendedOptions = options();
|
private static final Set<SocketOption<?>> extendedOptions = options();
|
||||||
|
|
||||||
static Set<SocketOption<?>> options() {
|
static Set<SocketOption<?>> options() {
|
||||||
|
@ -226,6 +249,7 @@ public final class ExtendedSocketOptions {
|
||||||
if (peerCredentialsSupported) {
|
if (peerCredentialsSupported) {
|
||||||
options.add(SO_PEERCRED);
|
options.add(SO_PEERCRED);
|
||||||
}
|
}
|
||||||
|
options.add(IP_DONTFRAGMENT);
|
||||||
return Collections.unmodifiableSet(options);
|
return Collections.unmodifiableSet(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,6 +272,8 @@ public final class ExtendedSocketOptions {
|
||||||
setQuickAckOption(fd, (boolean) value);
|
setQuickAckOption(fd, (boolean) value);
|
||||||
} else if (option == TCP_KEEPCOUNT) {
|
} else if (option == TCP_KEEPCOUNT) {
|
||||||
setTcpkeepAliveProbes(fd, (Integer) value);
|
setTcpkeepAliveProbes(fd, (Integer) value);
|
||||||
|
} else if (option == IP_DONTFRAGMENT) {
|
||||||
|
setIpDontFragment(fd, (Boolean) value);
|
||||||
} else if (option == TCP_KEEPIDLE) {
|
} else if (option == TCP_KEEPIDLE) {
|
||||||
setTcpKeepAliveTime(fd, (Integer) value);
|
setTcpKeepAliveTime(fd, (Integer) value);
|
||||||
} else if (option == TCP_KEEPINTERVAL) {
|
} else if (option == TCP_KEEPINTERVAL) {
|
||||||
|
@ -277,6 +303,8 @@ public final class ExtendedSocketOptions {
|
||||||
return getQuickAckOption(fd);
|
return getQuickAckOption(fd);
|
||||||
} else if (option == TCP_KEEPCOUNT) {
|
} else if (option == TCP_KEEPCOUNT) {
|
||||||
return getTcpkeepAliveProbes(fd);
|
return getTcpkeepAliveProbes(fd);
|
||||||
|
} else if (option == IP_DONTFRAGMENT) {
|
||||||
|
return getIpDontFragment(fd);
|
||||||
} else if (option == TCP_KEEPIDLE) {
|
} else if (option == TCP_KEEPIDLE) {
|
||||||
return getTcpKeepAliveTime(fd);
|
return getTcpKeepAliveTime(fd);
|
||||||
} else if (option == TCP_KEEPINTERVAL) {
|
} else if (option == TCP_KEEPINTERVAL) {
|
||||||
|
@ -320,6 +348,11 @@ public final class ExtendedSocketOptions {
|
||||||
platformSocketOptions.setTcpKeepAliveTime(fdAccess.get(fd), value);
|
platformSocketOptions.setTcpKeepAliveTime(fdAccess.get(fd), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void setIpDontFragment(FileDescriptor fd, boolean value)
|
||||||
|
throws SocketException {
|
||||||
|
platformSocketOptions.setIpDontFragment(fdAccess.get(fd), value);
|
||||||
|
}
|
||||||
|
|
||||||
private static void setTcpKeepAliveIntvl(FileDescriptor fd, int value)
|
private static void setTcpKeepAliveIntvl(FileDescriptor fd, int value)
|
||||||
throws SocketException {
|
throws SocketException {
|
||||||
platformSocketOptions.setTcpKeepAliveIntvl(fdAccess.get(fd), value);
|
platformSocketOptions.setTcpKeepAliveIntvl(fdAccess.get(fd), value);
|
||||||
|
@ -329,6 +362,10 @@ public final class ExtendedSocketOptions {
|
||||||
return platformSocketOptions.getTcpkeepAliveProbes(fdAccess.get(fd));
|
return platformSocketOptions.getTcpkeepAliveProbes(fdAccess.get(fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean getIpDontFragment(FileDescriptor fd) throws SocketException {
|
||||||
|
return platformSocketOptions.getIpDontFragment(fdAccess.get(fd));
|
||||||
|
}
|
||||||
|
|
||||||
private static int getTcpKeepAliveTime(FileDescriptor fd) throws SocketException {
|
private static int getTcpKeepAliveTime(FileDescriptor fd) throws SocketException {
|
||||||
return platformSocketOptions.getTcpKeepAliveTime(fdAccess.get(fd));
|
return platformSocketOptions.getTcpKeepAliveTime(fdAccess.get(fd));
|
||||||
}
|
}
|
||||||
|
@ -368,6 +405,8 @@ public final class ExtendedSocketOptions {
|
||||||
return newInstance("jdk.net.LinuxSocketOptions");
|
return newInstance("jdk.net.LinuxSocketOptions");
|
||||||
} else if (osname.startsWith("Mac")) {
|
} else if (osname.startsWith("Mac")) {
|
||||||
return newInstance("jdk.net.MacOSXSocketOptions");
|
return newInstance("jdk.net.MacOSXSocketOptions");
|
||||||
|
} else if (osname.startsWith("Windows")) {
|
||||||
|
return newInstance("jdk.net.WindowsSocketOptions");
|
||||||
} else {
|
} else {
|
||||||
return new PlatformSocketOptions();
|
return new PlatformSocketOptions();
|
||||||
}
|
}
|
||||||
|
@ -415,6 +454,14 @@ public final class ExtendedSocketOptions {
|
||||||
throw new UnsupportedOperationException("unsupported TCP_KEEPINTVL option");
|
throw new UnsupportedOperationException("unsupported TCP_KEEPINTVL option");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setIpDontFragment(int fd, final boolean value) throws SocketException {
|
||||||
|
throw new UnsupportedOperationException("unsupported IP_DONTFRAGMENT option");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean getIpDontFragment(int fd) throws SocketException {
|
||||||
|
throw new UnsupportedOperationException("unsupported IP_DONTFRAGMENT option");
|
||||||
|
}
|
||||||
|
|
||||||
int getTcpkeepAliveProbes(int fd) throws SocketException {
|
int getTcpkeepAliveProbes(int fd) throws SocketException {
|
||||||
throw new UnsupportedOperationException("unsupported TCP_KEEPCNT option");
|
throw new UnsupportedOperationException("unsupported TCP_KEEPCNT option");
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, 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. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package jdk.net;
|
||||||
|
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.security.AccessController;
|
||||||
|
import java.security.PrivilegedAction;
|
||||||
|
import jdk.net.ExtendedSocketOptions.PlatformSocketOptions;
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("removal")
|
||||||
|
class WindowsSocketOptions extends PlatformSocketOptions {
|
||||||
|
|
||||||
|
public WindowsSocketOptions() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void setIpDontFragment(int fd, final boolean value) throws SocketException {
|
||||||
|
setIpDontFragment0(fd, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean getIpDontFragment(int fd) throws SocketException {
|
||||||
|
return getIpDontFragment0(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static native void setIpDontFragment0(int fd, boolean value) throws SocketException;
|
||||||
|
private static native boolean getIpDontFragment0(int fd) throws SocketException;
|
||||||
|
|
||||||
|
static {
|
||||||
|
if (System.getSecurityManager() == null) {
|
||||||
|
System.loadLibrary("extnet");
|
||||||
|
} else {
|
||||||
|
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
|
||||||
|
System.loadLibrary("extnet");
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
126
src/jdk.net/windows/native/libextnet/WindowsSocketOptions.c
Normal file
126
src/jdk.net/windows/native/libextnet/WindowsSocketOptions.c
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022, 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. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winsock2.h>
|
||||||
|
|
||||||
|
#include <WS2tcpip.h>
|
||||||
|
|
||||||
|
#include "jni.h"
|
||||||
|
#include "jni_util.h"
|
||||||
|
#include "jvm.h"
|
||||||
|
|
||||||
|
static void handleError(JNIEnv *env, jint rv, const char *errmsg) {
|
||||||
|
if (rv < 0) {
|
||||||
|
if (errno == ENOPROTOOPT) {
|
||||||
|
JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
|
||||||
|
"unsupported socket option");
|
||||||
|
} else {
|
||||||
|
JNU_ThrowByNameWithLastError(env, "java/net/SocketException", 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
|
||||||
|
*/
|
||||||
|
JNIEXPORT void JNICALL Java_jdk_net_WindowsSocketOptions_setIpDontFragment0
|
||||||
|
(JNIEnv *env, jobject unused, jint fd, jboolean optval) {
|
||||||
|
int rv, opt;
|
||||||
|
jint family = socketFamily(fd);
|
||||||
|
if (family == -1) {
|
||||||
|
handleError(env, family, "get socket family failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (family == AF_INET) {
|
||||||
|
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) {
|
||||||
|
opt = optval;
|
||||||
|
rv = setsockopt(fd, IPPROTO_IP, IP_DONTFRAGMENT, (char *)&opt, sizeof(int));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
opt = optval ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
|
||||||
|
rv = setsockopt(fd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, (char *)&opt, sizeof(int));
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleError(env, rv, "set option IP_DONTFRAGMENT failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Class: jdk_net_WindowsSocketOptions
|
||||||
|
* Method: getIpDontFragment0
|
||||||
|
* Signature: (I)Z;
|
||||||
|
*/
|
||||||
|
JNIEXPORT jboolean JNICALL Java_jdk_net_WindowsSocketOptions_getIpDontFragment0
|
||||||
|
(JNIEnv *env, jobject unused, jint fd) {
|
||||||
|
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) {
|
||||||
|
rv = getsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, (char *)&optval, &sz);
|
||||||
|
if (rv == SOCKET_ERROR && WSAGetLastError() == WSAENOPROTOOPT) {
|
||||||
|
sz = sizeof(optval);
|
||||||
|
rv = getsockopt(fd, IPPROTO_IP, IP_DONTFRAGMENT, (char *)&optval, &sz);
|
||||||
|
handleError(env, rv, "get option IP_DONTFRAGMENT failed");
|
||||||
|
return optval;
|
||||||
|
}
|
||||||
|
handleError(env, rv, "get option IP_DONTFRAGMENT failed");
|
||||||
|
return optval == IP_PMTUDISC_DO ? JNI_TRUE : JNI_FALSE;
|
||||||
|
} else {
|
||||||
|
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);
|
||||||
|
handleError(env, rv, "get option IP_DONTFRAGMENT failed");
|
||||||
|
return optval;
|
||||||
|
}
|
||||||
|
handleError(env, rv, "get option IP_DONTFRAGMENT failed");
|
||||||
|
return optval == IP_PMTUDISC_DO ? JNI_TRUE : JNI_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -105,6 +105,8 @@ public class AfterClose {
|
||||||
map.put((SocketOption<?>)field.get(null), listOf(10, 100));
|
map.put((SocketOption<?>)field.get(null), listOf(10, 100));
|
||||||
field = c.getField("SO_INCOMING_NAPI_ID");
|
field = c.getField("SO_INCOMING_NAPI_ID");
|
||||||
map.put((SocketOption<?>)field.get(null), listOf(RO));
|
map.put((SocketOption<?>)field.get(null), listOf(RO));
|
||||||
|
field = c.getField("IP_DONTFRAGMENT");
|
||||||
|
map.put((SocketOption<?>)field.get(null), listOf(TRUE, FALSE));
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
// ignore, jdk.net module not present
|
// ignore, jdk.net module not present
|
||||||
} catch (ReflectiveOperationException e) {
|
} catch (ReflectiveOperationException e) {
|
||||||
|
|
106
test/jdk/jdk/net/ExtendedSocketOption/DontFragmentTest.java
Normal file
106
test/jdk/jdk/net/ExtendedSocketOption/DontFragmentTest.java
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, 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 8243099
|
||||||
|
* @modules jdk.net
|
||||||
|
* @run main/othervm DontFragmentTest ipv4
|
||||||
|
* @run main/othervm DontFragmentTest ipv6
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.*;
|
||||||
|
import java.nio.channels.*;
|
||||||
|
import static java.net.StandardProtocolFamily.INET;
|
||||||
|
import static java.net.StandardProtocolFamily.INET6;
|
||||||
|
import static jdk.net.ExtendedSocketOptions.IP_DONTFRAGMENT;
|
||||||
|
|
||||||
|
public class DontFragmentTest {
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
testDatagramChannel();
|
||||||
|
StandardProtocolFamily fam = args[0].equals("ipv4") ? INET : INET6;
|
||||||
|
System.out.println("Family = " + fam);
|
||||||
|
testDatagramChannel(args, fam);
|
||||||
|
try (DatagramSocket c = new DatagramSocket()) {
|
||||||
|
testDatagramSocket(c);
|
||||||
|
}
|
||||||
|
try (DatagramChannel dc = DatagramChannel.open(fam)) {
|
||||||
|
var c = dc.socket();
|
||||||
|
testDatagramSocket(c);
|
||||||
|
}
|
||||||
|
try (MulticastSocket mc = new MulticastSocket()) {
|
||||||
|
testDatagramSocket(mc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void testDatagramChannel() throws IOException {
|
||||||
|
try (DatagramChannel c1 = DatagramChannel.open()) {
|
||||||
|
|
||||||
|
if (c1.getOption(IP_DONTFRAGMENT)) {
|
||||||
|
throw new RuntimeException("IP_DONTFRAGMENT should not be set");
|
||||||
|
}
|
||||||
|
c1.setOption(IP_DONTFRAGMENT, true);
|
||||||
|
if (!c1.getOption(IP_DONTFRAGMENT)) {
|
||||||
|
throw new RuntimeException("IP_DONTFRAGMENT should be set");
|
||||||
|
}
|
||||||
|
c1.setOption(IP_DONTFRAGMENT, false);
|
||||||
|
if (c1.getOption(IP_DONTFRAGMENT)) {
|
||||||
|
throw new RuntimeException("IP_DONTFRAGMENT should not be set");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void testDatagramChannel(String[] args, ProtocolFamily fam) throws IOException {
|
||||||
|
try (DatagramChannel c1 = DatagramChannel.open(fam)) {
|
||||||
|
|
||||||
|
if (c1.getOption(IP_DONTFRAGMENT)) {
|
||||||
|
throw new RuntimeException("IP_DONTFRAGMENT should not be set");
|
||||||
|
}
|
||||||
|
c1.setOption(IP_DONTFRAGMENT, true);
|
||||||
|
if (!c1.getOption(IP_DONTFRAGMENT)) {
|
||||||
|
throw new RuntimeException("IP_DONTFRAGMENT should be set");
|
||||||
|
}
|
||||||
|
c1.setOption(IP_DONTFRAGMENT, false);
|
||||||
|
if (c1.getOption(IP_DONTFRAGMENT)) {
|
||||||
|
throw new RuntimeException("IP_DONTFRAGMENT should not be set");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void testDatagramSocket(DatagramSocket c1) throws IOException {
|
||||||
|
if (c1.getOption(IP_DONTFRAGMENT)) {
|
||||||
|
throw new RuntimeException("IP_DONTFRAGMENT should not be set");
|
||||||
|
}
|
||||||
|
c1.setOption(IP_DONTFRAGMENT, true);
|
||||||
|
if (!c1.getOption(IP_DONTFRAGMENT)) {
|
||||||
|
throw new RuntimeException("IP_DONTFRAGMENT should be set");
|
||||||
|
}
|
||||||
|
c1.setOption(IP_DONTFRAGMENT, false);
|
||||||
|
if (c1.getOption(IP_DONTFRAGMENT)) {
|
||||||
|
throw new RuntimeException("IP_DONTFRAGMENT should not be set");
|
||||||
|
}
|
||||||
|
c1.close();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue