1 /*
   2  * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 
  27 #include "SolarisSocketOptions.h"
  28 
  29 static jfieldID sf_priority;
  30 static jfieldID sf_bandwidth;
  31 
  32 static int initialized = 0;
  33 
  34 /*
  35  * Declare library specific JNI_Onload entry if static build
  36  */
  37 DEF_STATIC_JNI_OnLoad
  38 
  39 /*
  40  * Class:     jdk_net_SolarisSocketOptions
  41  * Method:    init
  42  * Signature: ()V
  43  */
  44 JNIEXPORT void JNICALL Java_jdk_net_SolarisSocketOptions_init
  45   (JNIEnv *env, jclass unused)
  46 {
  47     if (!initialized) {
  48         jclass c = (*env)->FindClass(env, "jdk/net/SocketFlow");
  49         CHECK_NULL(c);
  50         sf_priority = (*env)->GetFieldID(env, c, "priority", "I");
  51         CHECK_NULL(sf_priority);
  52         sf_bandwidth = (*env)->GetFieldID(env, c, "bandwidth", "J");
  53         CHECK_NULL(sf_bandwidth);
  54         initialized = 1;
  55     }
  56 }
  57 
  58 /** Return the Status value. */
  59 static jint toStatus(int errval)
  60 {
  61     switch (errval) {
  62       case 0:           return jdk_net_SocketFlow_OK_VALUE;
  63       case EPERM:       return jdk_net_SocketFlow_NO_PERMISSION_VALUE;
  64       case ENOTCONN:    return jdk_net_SocketFlow_NOT_CONNECTED_VALUE;
  65       case EOPNOTSUPP:  return jdk_net_SocketFlow_NOT_SUPPORTED_VALUE;
  66       case EALREADY:    return jdk_net_SocketFlow_ALREADY_CREATED_VALUE;
  67       case EINPROGRESS: return jdk_net_SocketFlow_IN_PROGRESS_VALUE;
  68       default:          return jdk_net_SocketFlow_OTHER_VALUE;
  69     }
  70 }
  71 
  72 void throwByNameWithLastError
  73   (JNIEnv *env, const char *name, const char *defaultDetail)
  74 {
  75   char defaultMsg[255];
  76   sprintf(defaultMsg, "errno: %d, %s", errno, defaultDetail);
  77   JNU_ThrowByNameWithLastError(env, name, defaultMsg);
  78 }
  79 
  80 /*
  81  * Class:     jdk_net_SolarisSocketOptions
  82  * Method:    setFlowOption0
  83  * Signature: (IIJ)I
  84  */
  85 JNIEXPORT jint JNICALL Java_jdk_net_SolarisSocketOptions_setFlowOption
  86   (JNIEnv *env, jobject unused, jint fd, jint priority, jlong bandwidth)
  87 {
  88     int rv;
  89     sock_flow_props_t props;
  90     memset(&props, 0, sizeof(props));
  91     props.sfp_version = SOCK_FLOW_PROP_VERSION1;
  92 
  93     if (priority != jdk_net_SocketFlow_UNSET) {
  94         props.sfp_mask |= SFP_PRIORITY;
  95         props.sfp_priority = priority;
  96     }
  97     if (bandwidth > jdk_net_SocketFlow_UNSET)  {
  98         props.sfp_mask |= SFP_MAXBW;
  99         props.sfp_maxbw = (uint64_t) bandwidth;
 100     }
 101 
 102     rv = setsockopt(fd, SOL_SOCKET, SO_FLOW_SLA, &props, sizeof(props));
 103 
 104     if (rv < 0) {
 105         if (errno == ENOPROTOOPT) {
 106             JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
 107                             "unsupported socket option");
 108         } else if (errno == EACCES || errno == EPERM) {
 109             JNU_ThrowByName(env, "java/net/SocketException", "Permission denied");
 110         } else {
 111             throwByNameWithLastError(env, "java/net/SocketException",
 112                                      "set option SO_FLOW_SLA failed");
 113         }
 114         return 0;
 115     }
 116     return toStatus(props.sfp_status);
 117 }
 118 
 119 /*
 120  * Class:     jdk_net_SolarisSocketOptions
 121  * Method:    getFlowOption0
 122  * Signature: (ILjdk/net/SocketFlow;)I
 123  */
 124 JNIEXPORT jint JNICALL Java_jdk_net_SolarisSocketOptions_getFlowOption
 125   (JNIEnv *env, jobject unused, jint fd, jobject flow)
 126 {
 127     sock_flow_props_t props;
 128     socklen_t sz = sizeof(props);
 129 
 130     int rv = getsockopt(fd, SOL_SOCKET, SO_FLOW_SLA, &props, &sz);
 131 
 132     if (rv < 0) {
 133         if (errno == ENOPROTOOPT) {
 134             JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
 135                             "unsupported socket option");
 136         } else if (errno == EACCES || errno == EPERM) {
 137             JNU_ThrowByName(env, "java/net/SocketException", "Permission denied");
 138         } else {
 139             throwByNameWithLastError(env, "java/net/SocketException",
 140                                      "get option SO_FLOW_SLA failed");
 141         }
 142         return -1;
 143     }
 144     /* first check status to see if flow exists */
 145     if (props.sfp_status == 0) { /* OK */
 146         /* can set the other fields now */
 147         if (props.sfp_mask & SFP_PRIORITY) {
 148             (*env)->SetIntField(env, flow, sf_priority, props.sfp_priority);
 149         }
 150         if (props.sfp_mask & SFP_MAXBW) {
 151             (*env)->SetLongField(env, flow, sf_bandwidth,
 152                                     (jlong)props.sfp_maxbw);
 153         }
 154     }
 155     return toStatus(props.sfp_status);
 156 }
 157 
 158 JNIEXPORT jboolean JNICALL Java_jdk_net_SolarisSocketOptions_flowSupported
 159   (JNIEnv *env, jobject unused)
 160 {
 161     /* Do a simple dummy call, and try to figure out from that */
 162     sock_flow_props_t props;
 163     int rv, s;
 164 
 165     s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
 166     if (s < 0) {
 167         return JNI_FALSE;
 168     }
 169     memset(&props, 0, sizeof(props));
 170     props.sfp_version = SOCK_FLOW_PROP_VERSION1;
 171     props.sfp_mask |= SFP_PRIORITY;
 172     props.sfp_priority = SFP_PRIO_NORMAL;
 173     rv = setsockopt(s, SOL_SOCKET, SO_FLOW_SLA, &props, sizeof(props));
 174     if (rv != 0 && errno == ENOPROTOOPT) {
 175         rv = JNI_FALSE;
 176     } else {
 177         rv = JNI_TRUE;
 178     }
 179     close(s);
 180     return rv;
 181 }