From 2a062f165491d599eb0dcfb6050eb9186ae31b71 Mon Sep 17 00:00:00 2001 From: Varada M Date: Mon, 17 Apr 2023 07:30:23 +0000 Subject: [PATCH] 8305089: Implement missing socket options on AIX Reviewed-by: erikj, jpai, vtewari, djelinski, dfuchs --- make/modules/jdk.net/Lib.gmk | 4 +- .../aix/classes/jdk/net/AIXSocketOptions.java | 143 ++++++++++ .../aix/native/libextnet/AIXSocketOptions.c | 265 ++++++++++++++++++ .../jdk/net/ExtendedSocketOptions.java | 1 + 4 files changed, 411 insertions(+), 2 deletions(-) create mode 100644 src/jdk.net/aix/classes/jdk/net/AIXSocketOptions.java create mode 100644 src/jdk.net/aix/native/libextnet/AIXSocketOptions.c diff --git a/make/modules/jdk.net/Lib.gmk b/make/modules/jdk.net/Lib.gmk index 2a43e81b355..ac7648644f8 100644 --- a/make/modules/jdk.net/Lib.gmk +++ b/make/modules/jdk.net/Lib.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2016, 2023, 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 @@ -27,7 +27,7 @@ include LibCommon.gmk ################################################################################ -ifeq ($(call isTargetOs, linux macosx windows), true) +ifeq ($(call isTargetOs, linux macosx windows aix), true) $(eval $(call SetupJdkLibrary, BUILD_LIBEXTNET, \ NAME := extnet, \ diff --git a/src/jdk.net/aix/classes/jdk/net/AIXSocketOptions.java b/src/jdk.net/aix/classes/jdk/net/AIXSocketOptions.java new file mode 100644 index 00000000000..0c061c06269 --- /dev/null +++ b/src/jdk.net/aix/classes/jdk/net/AIXSocketOptions.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2023, 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.nio.file.attribute.UserPrincipal; +import java.nio.file.attribute.GroupPrincipal; +import java.security.AccessController; +import java.security.PrivilegedAction; +import jdk.net.ExtendedSocketOptions.PlatformSocketOptions; +import sun.nio.fs.UnixUserPrincipals; + +@SuppressWarnings("removal") +class AIXSocketOptions extends PlatformSocketOptions { + + public AIXSocketOptions() { + } + + @Override + void setQuickAck(int fd, boolean on) throws SocketException { + setQuickAck0(fd, on); + } + + @Override + boolean getQuickAck(int fd) throws SocketException { + return getQuickAck0(fd); + } + + @Override + public boolean quickAckSupported() { + return quickAckSupported0(); + } + + @Override + boolean keepAliveOptionsSupported() { + return keepAliveOptionsSupported0(); + } + + @Override + boolean ipDontFragmentSupported() { + return true; + } + + boolean peerCredentialsSupported() { + return true; + } + + @Override + void setTcpkeepAliveProbes(int fd, final int value) throws SocketException { + setTcpkeepAliveProbes0(fd, value); + } + + @Override + void setTcpKeepAliveTime(int fd, final int value) throws SocketException { + setTcpKeepAliveTime0(fd, value); + } + + @Override + void setTcpKeepAliveIntvl(int fd, final int value) throws SocketException { + setTcpKeepAliveIntvl0(fd, value); + } + + @Override + int getTcpkeepAliveProbes(int fd) throws SocketException { + return getTcpkeepAliveProbes0(fd); + } + + @Override + int getTcpKeepAliveTime(int fd) throws SocketException { + return getTcpKeepAliveTime0(fd); + } + + @Override + int getTcpKeepAliveIntvl(int fd) throws SocketException { + return getTcpKeepAliveIntvl0(fd); + } + + @Override + void setIpDontFragment(int fd, final boolean value, boolean isIPv6) throws SocketException { + setIpDontFragment0(fd, value, isIPv6); + } + + @Override + boolean getIpDontFragment(int fd, boolean isIPv6) throws SocketException { + return getIpDontFragment0(fd, isIPv6); + } + + @Override + UnixDomainPrincipal getSoPeerCred(int fd) throws SocketException { + long l = getSoPeerCred0(fd); + int euid = (int)(l >> 32); + int egid = (int)l; + UserPrincipal user = UnixUserPrincipals.fromUid(euid); + GroupPrincipal group = UnixUserPrincipals.fromGid(egid); + return new UnixDomainPrincipal(user, group); + } + + 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, 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, 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; + private static native boolean keepAliveOptionsSupported0(); + private static native boolean quickAckSupported0(); + static { + if (System.getSecurityManager() == null) { + System.loadLibrary("extnet"); + } else { + AccessController.doPrivileged((PrivilegedAction) () -> { + System.loadLibrary("extnet"); + return null; + }); + } + } +} diff --git a/src/jdk.net/aix/native/libextnet/AIXSocketOptions.c b/src/jdk.net/aix/native/libextnet/AIXSocketOptions.c new file mode 100644 index 00000000000..503f48be32e --- /dev/null +++ b/src/jdk.net/aix/native/libextnet/AIXSocketOptions.c @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2023, 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include "jni_util.h" +#include "jdk_net_AIXSocketOptions.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 jint socketOptionSupported(jint level, jint optname) { + jint one = 1; + jint rv, s; + socklen_t sz = sizeof (one); + /* First try IPv6; fall back to IPv4. */ + s = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); + if (s < 0) { + if (errno == EPFNOSUPPORT || errno == EAFNOSUPPORT) { + s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + } + if (s < 0) { + return 0; + } + } + rv = getsockopt(s, level, optname, (void *) &one, &sz); + if (rv != 0 && errno == ENOPROTOOPT) { + rv = 0; + } else { + rv = 1; + } + close(s); + return rv; +} + +/* + * Declare library specific JNI_Onload entry if static build + */ +DEF_STATIC_JNI_OnLoad + +/* + * Class: jdk_net_AIXSocketOptions + * Method: setQuickAck + * Signature: (II)V + */ +JNIEXPORT void JNICALL Java_jdk_net_AIXSocketOptions_setQuickAck0 +(JNIEnv *env, jobject unused, jint fd, jboolean on) { + int optval; + int rv; + optval = (on ? 1 : 0); + rv = setsockopt(fd, SOL_SOCKET, TCP_NODELAYACK, &optval, sizeof (optval)); + handleError(env, rv, "set option TCP_NODELAYACK failed"); +} + +/* + * Class: jdk_net_AIXSocketOptions + * Method: getQuickAck + * Signature: (I)Z; + */ +JNIEXPORT jboolean JNICALL Java_jdk_net_AIXSocketOptions_getQuickAck0 +(JNIEnv *env, jobject unused, jint fd) { + int on; + socklen_t sz = sizeof (on); + int rv = getsockopt(fd, SOL_SOCKET, TCP_NODELAYACK, &on, &sz); + handleError(env, rv, "get option TCP_NODELAYACK failed"); + return on != 0; +} + +/* + * Class: jdk_net_AIXSocketOptions + * Method: quickAckSupported + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_jdk_net_AIXSocketOptions_quickAckSupported0 +(JNIEnv *env, jobject unused) { + return socketOptionSupported(SOL_SOCKET, TCP_NODELAYACK); +} + +/* + * Class: jdk_net_AIXSocketOptions + * Method: getSoPeerCred0 + * Signature: (I)L + */ +JNIEXPORT jlong JNICALL Java_jdk_net_AIXSocketOptions_getSoPeerCred0 + (JNIEnv *env, jclass clazz, jint fd) { + + int rv; + struct peercred_struct cred_info; + socklen_t len = sizeof(cred_info); + + if ((rv=getsockopt(fd, SOL_SOCKET, SO_PEERID, &cred_info, &len)) < 0) { + handleError(env, rv, "get SO_PEERID failed"); + } else { + if ((int)cred_info.euid == -1) { + handleError(env, -1, "get SO_PEERID failed"); + cred_info.euid = cred_info.egid = -1; + } + } + return (((jlong)cred_info.euid) << 32) | (cred_info.egid & 0xffffffffL); +} + +/* + * Class: jdk_net_AIXSocketOptions + * Method: keepAliveOptionsSupported0 + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_jdk_net_AIXSocketOptions_keepAliveOptionsSupported0 +(JNIEnv *env, jobject unused) { + return socketOptionSupported(IPPROTO_TCP, TCP_KEEPIDLE) && socketOptionSupported(IPPROTO_TCP, TCP_KEEPCNT) + && socketOptionSupported(IPPROTO_TCP, TCP_KEEPINTVL); +} + +/* + * Class: jdk_net_AIXSocketOptions + * Method: setTcpkeepAliveProbes0 + * Signature: (II)V + */ +JNIEXPORT void JNICALL Java_jdk_net_AIXSocketOptions_setTcpkeepAliveProbes0 +(JNIEnv *env, jobject unused, jint fd, jint optval) { + jint rv = setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, sizeof (optval)); + handleError(env, rv, "set option TCP_KEEPCNT failed"); +} + +/* + * Class: jdk_net_AIXSocketOptions + * Method: setTcpKeepAliveTime0 + * Signature: (II)V + */ +JNIEXPORT void JNICALL Java_jdk_net_AIXSocketOptions_setTcpKeepAliveTime0 +(JNIEnv *env, jobject unused, jint fd, jint optval) { + jint rv = setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, sizeof (optval)); + handleError(env, rv, "set option TCP_KEEPIDLE failed"); +} + +/* + * Class: jdk_net_AIXSocketOptions + * Method: setTcpKeepAliveIntvl0 + * Signature: (II)V + */ +JNIEXPORT void JNICALL Java_jdk_net_AIXSocketOptions_setTcpKeepAliveIntvl0 +(JNIEnv *env, jobject unused, jint fd, jint optval) { + jint rv = setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, sizeof (optval)); + handleError(env, rv, "set option TCP_KEEPINTVL failed"); +} + +/* + * Class: jdk_net_AIXSocketOptions + * Method: getTcpkeepAliveProbes0 + * Signature: (I)I; + */ +JNIEXPORT jint JNICALL Java_jdk_net_AIXSocketOptions_getTcpkeepAliveProbes0 +(JNIEnv *env, jobject unused, jint fd) { + jint optval, rv; + socklen_t sz = sizeof (optval); + rv = getsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, &sz); + handleError(env, rv, "get option TCP_KEEPCNT failed"); + return optval; +} + +/* + * Class: jdk_net_AIXSocketOptions + * Method: getTcpKeepAliveTime0 + * Signature: (I)I; + */ +JNIEXPORT jint JNICALL Java_jdk_net_AIXSocketOptions_getTcpKeepAliveTime0 +(JNIEnv *env, jobject unused, jint fd) { + jint optval, rv; + socklen_t sz = sizeof (optval); + rv = getsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, &sz); + handleError(env, rv, "get option TCP_KEEPIDLE failed"); + return optval; +} + +/* + * Class: jdk_net_AIXSocketOptions + * Method: getTcpKeepAliveIntvl0 + * Signature: (I)I; + */ +JNIEXPORT jint JNICALL Java_jdk_net_AIXSocketOptions_getTcpKeepAliveIntvl0 +(JNIEnv *env, jobject unused, jint fd) { + jint optval, rv; + socklen_t sz = sizeof (optval); + rv = getsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, &sz); + handleError(env, rv, "get option TCP_KEEPINTVL failed"); + return optval; +} + +/* + * Class: jdk_net_AIXSocketOptions + * Method: setIpDontFragment0 + * Signature: (IZZ)V + */ +JNIEXPORT void JNICALL Java_jdk_net_AIXSocketOptions_setIpDontFragment0 +(JNIEnv *env, jobject unused, jint fd, jboolean optval, jboolean isIPv6) { + jint rv, optsetting; + + //optsetting = optval ? IP_PMTUDISC_DO/IP_DONTFRAG : IP_PMTUDISC_DONT; + optsetting = optval ? 1 : 0; + if (!isIPv6) { + rv = setsockopt(fd, IPPROTO_IP, IP_DONTFRAG, &optsetting, sizeof (optsetting)); + } else { + rv = setsockopt(fd, IPPROTO_IPV6, IPV6_DONTFRAG, &optsetting, sizeof (optsetting)); + } + handleError(env, rv, "set option IP_DONTFRAGMENT failed"); +} + +/* + * Class: jdk_net_AIXSocketOptions + * Method: getIpDontFragment0 + * Signature: (IZ)Z; + */ +JNIEXPORT jboolean JNICALL Java_jdk_net_AIXSocketOptions_getIpDontFragment0 +(JNIEnv *env, jobject unused, jint fd, jboolean isIPv6) { + jint optlevel, optname, optval, rv; + + if (!isIPv6) { + optlevel = IPPROTO_IP; + optname = IP_DONTFRAG; + } else { + optlevel = IPPROTO_IPV6; + optname = IPV6_DONTFRAG; + } + socklen_t sz = sizeof(optval); + rv = getsockopt(fd, optlevel, optname, &optval, &sz); + handleError(env, rv, "get option IP_DONTFRAGMENT failed"); + return optval > 0 ? JNI_TRUE : JNI_FALSE; +} diff --git a/src/jdk.net/share/classes/jdk/net/ExtendedSocketOptions.java b/src/jdk.net/share/classes/jdk/net/ExtendedSocketOptions.java index eb37d26bff7..fc06d7c4349 100644 --- a/src/jdk.net/share/classes/jdk/net/ExtendedSocketOptions.java +++ b/src/jdk.net/share/classes/jdk/net/ExtendedSocketOptions.java @@ -403,6 +403,7 @@ public final class ExtendedSocketOptions { case LINUX -> newInstance("jdk.net.LinuxSocketOptions"); case MACOS -> newInstance("jdk.net.MacOSXSocketOptions"); case WINDOWS -> newInstance("jdk.net.WindowsSocketOptions"); + case AIX -> newInstance("jdk.net.AIXSocketOptions"); default -> new PlatformSocketOptions(); }; }