Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/native/java/net/ExtendedOptionsImpl.c
32287 views
/*1* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include <jni.h>26#include <string.h>27#if defined(__linux__) || defined(MACOSX)28#include <netinet/tcp.h>29#include <netinet/in.h>30#endif3132#include "net_util.h"33#include "jdk_net_SocketFlow.h"3435static jclass sf_status_class; /* Status enum type */3637static jfieldID sf_status;38static jfieldID sf_priority;39static jfieldID sf_bandwidth;4041static jfieldID sf_fd_fdID; /* FileDescriptor.fd */4243/* References to the literal enum values */4445static jobject sfs_NOSTATUS;46static jobject sfs_OK;47static jobject sfs_NOPERMISSION;48static jobject sfs_NOTCONNECTED;49static jobject sfs_NOTSUPPORTED;50static jobject sfs_ALREADYCREATED;51static jobject sfs_INPROGRESS;52static jobject sfs_OTHER;5354static jobject getEnumField(JNIEnv *env, char *name);55static void setStatus(JNIEnv *env, jobject obj, int errval);5657/* OS specific code is implemented in these three functions */5859static jboolean flowSupported0() ;6061/*62* Class: sun_net_ExtendedOptionsImpl63* Method: init64* Signature: ()V65*/66JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_init67(JNIEnv *env, jclass UNUSED) {6869static int initialized = 0;70jclass c;7172/* Global class references */7374if (initialized) {75return;76}7778c = (*env)->FindClass(env, "jdk/net/SocketFlow$Status");79CHECK_NULL(c);80sf_status_class = (*env)->NewGlobalRef(env, c);81CHECK_NULL(sf_status_class);8283/* int "fd" field of java.io.FileDescriptor */8485c = (*env)->FindClass(env, "java/io/FileDescriptor");86CHECK_NULL(c);87sf_fd_fdID = (*env)->GetFieldID(env, c, "fd", "I");88CHECK_NULL(sf_fd_fdID);899091/* SocketFlow fields */9293c = (*env)->FindClass(env, "jdk/net/SocketFlow");94CHECK_NULL(c);9596/* status */9798sf_status = (*env)->GetFieldID(env, c, "status",99"Ljdk/net/SocketFlow$Status;");100CHECK_NULL(sf_status);101102/* priority */103104sf_priority = (*env)->GetFieldID(env, c, "priority", "I");105CHECK_NULL(sf_priority);106107/* bandwidth */108109sf_bandwidth = (*env)->GetFieldID(env, c, "bandwidth", "J");110CHECK_NULL(sf_bandwidth);111112/* Initialize the static enum values */113114sfs_NOSTATUS = getEnumField(env, "NO_STATUS");115CHECK_NULL(sfs_NOSTATUS);116sfs_OK = getEnumField(env, "OK");117CHECK_NULL(sfs_OK);118sfs_NOPERMISSION = getEnumField(env, "NO_PERMISSION");119CHECK_NULL(sfs_NOPERMISSION);120sfs_NOTCONNECTED = getEnumField(env, "NOT_CONNECTED");121CHECK_NULL(sfs_NOTCONNECTED);122sfs_NOTSUPPORTED = getEnumField(env, "NOT_SUPPORTED");123CHECK_NULL(sfs_NOTSUPPORTED);124sfs_ALREADYCREATED = getEnumField(env, "ALREADY_CREATED");125CHECK_NULL(sfs_ALREADYCREATED);126sfs_INPROGRESS = getEnumField(env, "IN_PROGRESS");127CHECK_NULL(sfs_INPROGRESS);128sfs_OTHER = getEnumField(env, "OTHER");129CHECK_NULL(sfs_OTHER);130initialized = JNI_TRUE;131}132133static jobject getEnumField(JNIEnv *env, char *name) {134jobject f;135jfieldID fID = (*env)->GetStaticFieldID(env, sf_status_class, name,136"Ljdk/net/SocketFlow$Status;");137CHECK_NULL_RETURN(fID, NULL);138139f = (*env)->GetStaticObjectField(env, sf_status_class, fID);140CHECK_NULL_RETURN(f, NULL);141f = (*env)->NewGlobalRef(env, f);142CHECK_NULL_RETURN(f, NULL);143return f;144}145146/*147* Retrieve the int file-descriptor from a public socket type object.148* Gets impl, then the FileDescriptor from the impl, and then the fd149* from that.150*/151static int getFD(JNIEnv *env, jobject fileDesc) {152return (*env)->GetIntField(env, fileDesc, sf_fd_fdID);153}154155/**156* Sets the status field of a SocketFlow to one of the157* canned enum values158*/159static void setStatus (JNIEnv *env, jobject obj, int errval) {160switch (errval) {161case 0: /* OK */162(*env)->SetObjectField(env, obj, sf_status, sfs_OK);163break;164case EPERM:165(*env)->SetObjectField(env, obj, sf_status, sfs_NOPERMISSION);166break;167case ENOTCONN:168(*env)->SetObjectField(env, obj, sf_status, sfs_NOTCONNECTED);169break;170case EOPNOTSUPP:171(*env)->SetObjectField(env, obj, sf_status, sfs_NOTSUPPORTED);172break;173case EALREADY:174(*env)->SetObjectField(env, obj, sf_status, sfs_ALREADYCREATED);175break;176case EINPROGRESS:177(*env)->SetObjectField(env, obj, sf_status, sfs_INPROGRESS);178break;179default:180(*env)->SetObjectField(env, obj, sf_status, sfs_OTHER);181break;182}183}184185#ifdef __solaris__186187/*188* Class: sun_net_ExtendedOptionsImpl189* Method: setFlowOption190* Signature: (Ljava/io/FileDescriptor;Ljdk/net/SocketFlow;)V191*/192JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setFlowOption193(JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) {194int fd = getFD(env, fileDesc);195196if (fd < 0) {197NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed");198return;199} else {200sock_flow_props_t props;201jlong bandwidth;202int rv;203204jint priority = (*env)->GetIntField(env, flow, sf_priority);205memset(&props, 0, sizeof(props));206props.sfp_version = SOCK_FLOW_PROP_VERSION1;207208if (priority != jdk_net_SocketFlow_UNSET) {209props.sfp_mask |= SFP_PRIORITY;210props.sfp_priority = priority;211}212bandwidth = (*env)->GetLongField(env, flow, sf_bandwidth);213if (bandwidth > -1) {214props.sfp_mask |= SFP_MAXBW;215props.sfp_maxbw = (uint64_t) bandwidth;216}217rv = setsockopt(fd, SOL_SOCKET, SO_FLOW_SLA, &props, sizeof(props));218if (rv < 0) {219if (errno == ENOPROTOOPT) {220JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",221"unsupported socket option");222} else if (errno == EACCES || errno == EPERM) {223NET_ERROR(env, JNU_JAVANETPKG "SocketException",224"Permission denied");225} else {226NET_ERROR(env, JNU_JAVANETPKG "SocketException",227"set option SO_FLOW_SLA failed");228}229return;230}231setStatus(env, flow, props.sfp_status);232}233}234235/*236* Class: sun_net_ExtendedOptionsImpl237* Method: getFlowOption238* Signature: (Ljava/io/FileDescriptor;Ljdk/net/SocketFlow;)V239*/240JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_getFlowOption241(JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) {242int fd = getFD(env, fileDesc);243244if (fd < 0) {245NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed");246return;247} else {248sock_flow_props_t props;249int status;250socklen_t sz = sizeof(props);251252int rv = getsockopt(fd, SOL_SOCKET, SO_FLOW_SLA, &props, &sz);253if (rv < 0) {254if (errno == ENOPROTOOPT) {255JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",256"unsupported socket option");257} else if (errno == EACCES || errno == EPERM) {258NET_ERROR(env, JNU_JAVANETPKG "SocketException",259"Permission denied");260} else {261NET_ERROR(env, JNU_JAVANETPKG "SocketException",262"set option SO_FLOW_SLA failed");263}264return;265}266/* first check status to see if flow exists */267status = props.sfp_status;268setStatus(env, flow, status);269if (status == 0) { /* OK */270/* can set the other fields now */271if (props.sfp_mask & SFP_PRIORITY) {272(*env)->SetIntField(env, flow, sf_priority, props.sfp_priority);273}274if (props.sfp_mask & SFP_MAXBW) {275(*env)->SetLongField(env, flow, sf_bandwidth,276(jlong)props.sfp_maxbw);277}278}279}280}281282static jboolean flowsupported;283static jboolean flowsupported_set = JNI_FALSE;284285static jboolean flowSupported0() {286/* Do a simple dummy call, and try to figure out from that */287sock_flow_props_t props;288int rv, s;289if (flowsupported_set) {290return flowsupported;291}292s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);293if (s < 0) {294flowsupported = JNI_FALSE;295flowsupported_set = JNI_TRUE;296return JNI_FALSE;297}298memset(&props, 0, sizeof(props));299props.sfp_version = SOCK_FLOW_PROP_VERSION1;300props.sfp_mask |= SFP_PRIORITY;301props.sfp_priority = SFP_PRIO_NORMAL;302rv = setsockopt(s, SOL_SOCKET, SO_FLOW_SLA, &props, sizeof(props));303if (rv != 0 && errno == ENOPROTOOPT) {304rv = JNI_FALSE;305} else {306rv = JNI_TRUE;307}308close(s);309flowsupported = rv;310flowsupported_set = JNI_TRUE;311return flowsupported;312}313314#else /* __solaris__ */315316/* Non Solaris. Functionality is not supported. So, throw UnsupportedOpExc */317318JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setFlowOption319(JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) {320JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",321"unsupported socket option");322}323324JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_getFlowOption325(JNIEnv *env, jclass UNUSED, jobject fileDesc, jobject flow) {326JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",327"unsupported socket option");328}329330static jboolean flowSupported0() {331return JNI_FALSE;332}333334#endif /* __solaris__ */335336// Keep alive options are available for MACOSX and Linux only for337// the time being.338#if defined(__linux__) || defined(MACOSX)339340// Map socket option level/name to OS specific constant341#ifdef __linux__342#define SOCK_OPT_LEVEL SOL_TCP343#define SOCK_OPT_NAME_KEEPIDLE TCP_KEEPIDLE344#define SOCK_OPT_NAME_KEEPIDLE_STR "TCP_KEEPIDLE"345#endif346#ifdef MACOSX347#define SOCK_OPT_LEVEL IPPROTO_TCP348#define SOCK_OPT_NAME_KEEPIDLE TCP_KEEPALIVE349#define SOCK_OPT_NAME_KEEPIDLE_STR "TCP_KEEPALIVE"350#endif351352static jint socketOptionSupported(jint sockopt) {353jint one = 1;354jint rv, s;355s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);356if (s < 0) {357return 0;358}359rv = setsockopt(s, SOCK_OPT_LEVEL, sockopt, (void *) &one, sizeof (one));360if (rv != 0 && errno == ENOPROTOOPT) {361rv = 0;362} else {363rv = 1;364}365close(s);366return rv;367}368369static void handleError(JNIEnv *env, jint rv, const char *errmsg) {370if (rv < 0) {371if (errno == ENOPROTOOPT) {372JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",373"unsupported socket option");374} else {375JNU_ThrowByNameWithLastError(env, "java/net/SocketException", errmsg);376}377}378}379380static void setTcpSocketOption381(JNIEnv *env, jobject fileDesc, jint optval, int opt, int optlevel, const char* errmsg) {382int fd = getFD(env, fileDesc);383384if (fd < 0) {385NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed");386return;387} else {388jint rv = setsockopt(fd, optlevel, opt, &optval, sizeof (optval));389handleError(env, rv, errmsg);390}391}392393static jint getTcpSocketOption394(JNIEnv *env, jobject fileDesc, int opt, int optlevel, const char* errmsg) {395int fd = getFD(env, fileDesc);396397if (fd < 0) {398NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed");399return -1;400} else {401jint optval, rv;402socklen_t sz = sizeof (optval);403rv = getsockopt(fd, optlevel, opt, &optval, &sz);404handleError(env, rv, errmsg);405return optval;406}407}408409#else /* __linux__ || MACOSX */410411// On AIX and Solaris these constants aren't defined. Map them to a412// value so that the code below compiles. Values aren't used as413// on those platforms UnsupportedOperationException will be thrown414// instead.415#define SOCK_OPT_LEVEL -1416#define SOCK_OPT_NAME_KEEPIDLE -1417#define SOCK_OPT_NAME_KEEPIDLE_STR "TCP_KEEPIDLE"418#define TCP_KEEPCNT -1419#define TCP_KEEPINTVL -1420#define SOCK_OPT_LEVEL -1421#define SOCK_OPT_NAME_KEEPIDLE -1422423/* Keep alive options not supported for non-linux/non-macosx so throw UnsupportedOpExc */424425static void setTcpSocketOption426(JNIEnv *env, jobject fileDesc, jint optval, int opt, int optlevel, const char* errmsg) {427JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",428"unsupported socket option");429}430431static jint getTcpSocketOption432(JNIEnv *env, jobject fileDesc, int opt, int optlevel, const char* errmsg) {433JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",434"unsupported socket option");435}436437#endif /* __linux__ || MACOSX*/438439JNIEXPORT jboolean JNICALL Java_sun_net_ExtendedOptionsImpl_flowSupported440(JNIEnv *env, jclass UNUSED) {441return flowSupported0();442}443444#if defined(__linux__) || defined(MACOSX)445446/*447* Class: sun_net_ExtendedOptionsImpl448* Method: keepAliveOptionsSupported449* Signature: ()Z450*/451JNIEXPORT jboolean JNICALL Java_sun_net_ExtendedOptionsImpl_keepAliveOptionsSupported452(JNIEnv *env, jobject unused) {453return socketOptionSupported(SOCK_OPT_NAME_KEEPIDLE) && socketOptionSupported(TCP_KEEPCNT)454&& socketOptionSupported(TCP_KEEPINTVL);455}456457#else458459/*460* Class: sun_net_ExtendedOptionsImpl461* Method: keepAliveOptionsSupported462* Signature: ()Z463*/464JNIEXPORT jboolean JNICALL Java_sun_net_ExtendedOptionsImpl_keepAliveOptionsSupported465(JNIEnv *env, jobject unused) {466return JNI_FALSE;467}468469#endif /* __linux__ || MACOSX */470471/*472* Class: sun_net_ExtendedOptionsImpl473* Method: setTcpKeepAliveProbes474* Signature: (Ljava/io/FileDescriptor;I)V475*/476JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setTcpKeepAliveProbes477(JNIEnv *env, jobject unused, jobject fileDesc, jint optval) {478setTcpSocketOption(env, fileDesc, optval, TCP_KEEPCNT, SOCK_OPT_LEVEL,479"set option TCP_KEEPCNT failed");480}481482/*483* Class: sun_net_ExtendedOptionsImpl484* Method: setTcpKeepAliveTime485* Signature: (Ljava/io/FileDescriptor;I)V486*/487JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setTcpKeepAliveTime488(JNIEnv *env, jobject unused, jobject fileDesc, jint optval) {489setTcpSocketOption(env, fileDesc, optval, SOCK_OPT_NAME_KEEPIDLE, SOCK_OPT_LEVEL,490"set option " SOCK_OPT_NAME_KEEPIDLE_STR " failed");491}492493/*494* Class: sun_net_ExtendedOptionsImpl495* Method: setTcpKeepAliveIntvl496* Signature: (Ljava/io/FileDescriptor;I)V497*/498JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setTcpKeepAliveIntvl499(JNIEnv *env, jobject unused, jobject fileDesc, jint optval) {500setTcpSocketOption(env, fileDesc, optval, TCP_KEEPINTVL, SOCK_OPT_LEVEL,501"set option TCP_KEEPINTVL failed");502}503504/*505* Class: sun_net_ExtendedOptionsImpl506* Method: getTcpKeepAliveProbes507* Signature: (Ljava/io/FileDescriptor;)I508*/509JNIEXPORT jint JNICALL Java_sun_net_ExtendedOptionsImpl_getTcpKeepAliveProbes510(JNIEnv *env, jobject unused, jobject fileDesc) {511return getTcpSocketOption(env, fileDesc, TCP_KEEPCNT, SOCK_OPT_LEVEL,512"get option TCP_KEEPCNT failed");513}514515/*516* Class: sun_net_ExtendedOptionsImpl517* Method: getTcpKeepAliveTime518* Signature: (Ljava/io/FileDescriptor;)I519*/520JNIEXPORT jint JNICALL Java_sun_net_ExtendedOptionsImpl_getTcpKeepAliveTime521(JNIEnv *env, jobject unused, jobject fileDesc) {522return getTcpSocketOption(env, fileDesc, SOCK_OPT_NAME_KEEPIDLE, SOCK_OPT_LEVEL,523"get option " SOCK_OPT_NAME_KEEPIDLE_STR " failed");524}525526/*527* Class: sun_net_ExtendedOptionsImpl528* Method: getTcpKeepAliveIntvl529* Signature: (Ljava/io/FileDescriptor;)I530*/531JNIEXPORT jint JNICALL Java_sun_net_ExtendedOptionsImpl_getTcpKeepAliveIntvl532(JNIEnv *env, jobject unused, jobject fileDesc) {533return getTcpSocketOption(env, fileDesc, TCP_KEEPINTVL, SOCK_OPT_LEVEL,534"get option TCP_KEEPINTVL failed");535}536537538