1 /*
   2  * Copyright (c) 2018, 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 #include "net_util.h"
  27 #include "nio.h"
  28 #include "nio_util.h"
  29 #include "rdma_util_md.h"
  30 #include "Rsocket.h"
  31 #include "sun_nio_ch_Net.h"
  32 
  33 static jfieldID fd_fdID;
  34 
  35 JNIEXPORT jboolean JNICALL
  36 Java_jdk_internal_net_rdma_RdmaNet_isRdmaAvailable0(JNIEnv *env, jclass cls) {
  37     return rdma_supported();
  38 }
  39 
  40 static int
  41 configureBlocking(int fd, jboolean blocking)
  42 {
  43     int flags = rs_fcntl(fd, F_GETFL);
  44     int newflags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
  45 
  46     return (flags == newflags) ? 0 : rs_fcntl(fd, F_SETFL, newflags);
  47 }
  48 
  49 JNIEXPORT void JNICALL
  50 Java_jdk_internal_net_rdma_RdmaNet_configureBlocking(JNIEnv *env, jclass clazz,
  51                                          jobject fdo, jboolean blocking)
  52 {
  53     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
  54     if (configureBlocking(fd, blocking) < 0)
  55         JNU_ThrowIOExceptionWithLastError(env, "Configure blocking failed");
  56 }
  57 
  58 JNIEXPORT void JNICALL
  59 Java_jdk_internal_net_rdma_RdmaNet_initIDs(JNIEnv *env, jclass clazz)
  60 {
  61     loadRdmaFuncs(env);
  62     CHECK_NULL(clazz = (*env)->FindClass(env, "java/io/FileDescriptor"));
  63     CHECK_NULL(fd_fdID = (*env)->GetFieldID(env, clazz, "fd", "I"));
  64     initInetAddressIDs(env);
  65 }
  66 
  67 JNIEXPORT jboolean JNICALL
  68 Java_jdk_internal_net_rdma_RdmaNet_isIPv6Available0(JNIEnv* env, jclass cl)
  69 {
  70     return (ipv6_available()) ? JNI_TRUE : JNI_FALSE;
  71 }
  72 
  73 JNIEXPORT jboolean JNICALL
  74 Java_jdk_internal_net_rdma_RdmaNet_canIPv6SocketJoinIPv4Group0(JNIEnv* env, jclass cl)
  75 {
  76     return JNI_TRUE;
  77 }
  78 
  79 JNIEXPORT jboolean JNICALL
  80 Java_jdk_internal_net_rdma_RdmaNet_canJoin6WithIPv4Group0(JNIEnv* env, jclass cl)
  81 {
  82     return JNI_FALSE;
  83 }
  84 
  85 JNIEXPORT jint JNICALL
  86 Java_jdk_internal_net_rdma_RdmaNet_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6,
  87                             jboolean stream, jboolean reuse, jboolean ignored)
  88 {
  89     int fd;
  90     int type = (stream ? SOCK_STREAM : SOCK_DGRAM);
  91     int domain = (ipv6_available() && preferIPv6) ? AF_INET6 : AF_INET;
  92 
  93     fd = rs_socket(domain, type, 0);
  94     if (fd < 0) {
  95         return handleSocketError(env, errno);
  96     }
  97 
  98     if (reuse) {
  99         int arg = 1;
 100         if (rs_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg,
 101                        sizeof(arg)) < 0) {
 102             JNU_ThrowByNameWithLastError(env,
 103                                          JNU_JAVANETPKG "SocketException",
 104                                          "Unable to set SO_REUSEADDR");
 105             rs_close(fd);
 106             return -1;
 107         }
 108     }
 109 
 110     return fd;
 111 }
 112 
 113 JNIEXPORT void JNICALL
 114 Java_jdk_internal_net_rdma_RdmaNet_bind0(JNIEnv *env, jclass clazz, jobject fdo, jboolean preferIPv6,
 115                           jboolean useExclBind, jobject iao, int port)
 116 {
 117     SOCKETADDRESS sa;
 118     int sa_len = 0;
 119     int rv = 0;
 120 
 121     if (NET_InetAddressToSockaddr(env, iao, port, &sa, &sa_len,
 122                                   preferIPv6) != 0) {
 123         return;
 124     }
 125 
 126     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 127     rv = RDMA_Bind(fd, &sa, sa_len);
 128     if (rv != 0) {
 129         handleSocketError(env, errno);
 130     }
 131 }
 132 
 133 JNIEXPORT void JNICALL
 134 Java_jdk_internal_net_rdma_RdmaNet_listen(JNIEnv *env, jclass cl, jobject fdo, jint backlog)
 135 {
 136     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 137     if (rs_listen(fd, backlog) < 0)
 138        handleSocketError(env, errno);
 139 }
 140 
 141 JNIEXPORT jint JNICALL
 142 Java_jdk_internal_net_rdma_RdmaNet_connect0(JNIEnv *env, jclass clazz, jboolean preferIPv6,
 143                              jobject fdo, jobject iao, jint port)
 144 {
 145     SOCKETADDRESS sa;
 146     int sa_len = 0;
 147     int rv;
 148 
 149     if (NET_InetAddressToSockaddr(env, iao, port, &sa, &sa_len,
 150                                   preferIPv6) != 0) {
 151         return IOS_THROWN;
 152     }
 153 
 154     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 155 
 156     rv = rs_connect(fd, &sa.sa, sa_len);
 157     if (rv != 0) {
 158         if (errno == EINPROGRESS) {
 159             return IOS_UNAVAILABLE;
 160         } else if (errno == EINTR) {
 161             return IOS_INTERRUPTED;
 162         }
 163         return handleSocketError(env, errno);
 164     }
 165     return 1;
 166 }
 167 
 168 JNIEXPORT jint JNICALL
 169 Java_jdk_internal_net_rdma_RdmaNet_localPort(JNIEnv *env, jclass clazz, jobject fdo)
 170 {
 171     SOCKETADDRESS sa;
 172     socklen_t sa_len = sizeof(SOCKETADDRESS);
 173     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 174     if (rs_getsockname(fd, &sa.sa, &sa_len) < 0) {
 175         handleSocketError(env, errno);
 176         return -1;
 177     }
 178     return NET_GetPortFromSockaddr(&sa);
 179 }
 180 
 181 JNIEXPORT jobject JNICALL
 182 Java_jdk_internal_net_rdma_RdmaNet_localInetAddress(JNIEnv *env, jclass clazz, jobject fdo)
 183 {
 184     SOCKETADDRESS sa;
 185     socklen_t sa_len = sizeof(SOCKETADDRESS);
 186     int port;
 187     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 188     if (rs_getsockname(fd, &sa.sa, &sa_len) < 0) {
 189         handleSocketError(env, errno);
 190         return NULL;
 191     }
 192     return NET_SockaddrToInetAddress(env, &sa, &port);
 193 }
 194 
 195 JNIEXPORT jint JNICALL
 196 Java_jdk_internal_net_rdma_RdmaNet_getIntOption0(JNIEnv *env, jclass clazz, jobject fdo,
 197                                    jboolean mayNeedConversion, jint level, jint opt)
 198 {
 199     int result;
 200     struct linger linger;
 201     u_char carg;
 202     void *arg;
 203     socklen_t arglen;
 204     int n;
 205 
 206     arg = (void *)&result;
 207     arglen = sizeof(result);
 208 
 209     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 210     if (mayNeedConversion) {
 211         n = RDMA_GetSockOpt(fd, level, opt, arg, (int*)&arglen);
 212     } else {
 213         n = rs_getsockopt(fd, level, opt, arg, &arglen);
 214     }
 215     if (n < 0) {
 216         JNU_ThrowByNameWithLastError(env,
 217                                      JNU_JAVANETPKG "SocketException",
 218                                      "jdk.internal.net.rdma.RdmaNet.getIntOption");
 219         return -1;
 220     }
 221 
 222     return (jint)result;
 223 }
 224 
 225 JNIEXPORT void JNICALL
 226 Java_jdk_internal_net_rdma_RdmaNet_setIntOption0(JNIEnv *env, jclass clazz, jobject fdo,
 227                                    jboolean mayNeedConversion, jint level,
 228                                    jint opt, jint arg, jboolean isIPv6)
 229 {
 230     int result;
 231     struct linger linger;
 232     u_char carg;
 233     void *parg;
 234     socklen_t arglen;
 235     int n;
 236 
 237     parg = (void*)&arg;
 238     arglen = sizeof(arg);
 239 
 240     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 241     if (mayNeedConversion) {
 242         n = RDMA_SetSockOpt(fd, level, opt, parg, arglen);
 243     } else {
 244         n = rs_setsockopt(fd, level, opt, parg, arglen);
 245     }
 246     if (n < 0) {
 247         JNU_ThrowByNameWithLastError(env,
 248                                      JNU_JAVANETPKG "SocketException",
 249                                      "jdk.internal.net.rdma.RdmaNet.setIntOption");
 250     }
 251 }
 252 
 253 JNIEXPORT void JNICALL
 254 Java_jdk_internal_net_rdma_RdmaNet_shutdown(JNIEnv *env, jclass cl, jobject fdo, jint jhow)
 255 {
 256     int how = (jhow == sun_nio_ch_Net_SHUT_RD) ? SHUT_RD :
 257         (jhow == sun_nio_ch_Net_SHUT_WR) ? SHUT_WR : SHUT_RDWR;
 258     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 259     if ((rs_shutdown(fd, how) < 0) && (errno != ENOTCONN))
 260         handleSocketError(env, errno);
 261 }
 262 
 263 JNIEXPORT jint JNICALL
 264 Java_jdk_internal_net_rdma_RdmaNet_poll(JNIEnv* env, jclass this, jobject fdo, jint events, jlong timeout)
 265 {
 266     struct pollfd pfd;
 267     int rv;
 268     pfd.fd = (*env)->GetIntField(env, fdo, fd_fdID);
 269     pfd.events = events;
 270     if (timeout < -1) {
 271         timeout = -1;
 272     } else if (timeout > INT_MAX) {
 273         timeout = INT_MAX;
 274     }
 275     rv = rs_poll(&pfd, 1, (int)timeout);
 276 
 277     if (rv >= 0) {
 278         return pfd.revents;
 279     } else if (errno == EINTR) {
 280         // interrupted, no events to return
 281         return 0;
 282     } else {
 283         handleSocketError(env, errno);
 284         return IOS_THROWN;
 285     }
 286 }