1 /*
   2  * Copyright (c) 2001, 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 <poll.h>
  27 #include <sys/types.h>
  28 #include <sys/socket.h>
  29 #include <rdma/rsocket.h>
  30 #include <string.h>
  31 #include <netinet/in.h>
  32 #include <netinet/tcp.h>
  33 #include <limits.h>
  34 
  35 #include "jni.h"
  36 #include "jni_util.h"
  37 #include "jvm.h"
  38 #include "jlong.h"
  39 #include "sun_nio_ch_Net.h"
  40 #include "net_util.h"
  41 #include "net_util_md.h"
  42 #include "nio_util.h"
  43 #include "nio.h"
  44 #include "rdma_util_md.h"
  45 
  46 #ifndef IP_MULTICAST_ALL
  47   #define IP_MULTICAST_ALL    49
  48 #endif
  49 
  50 #define COPY_INET6_ADDRESS(env, source, target) \
  51     (*env)->GetByteArrayRegion(env, source, 0, 16, target)
  52 
  53 static jfieldID fd_fdID;
  54 
  55 JNIEXPORT jboolean JNICALL
  56 Java_rdma_ch_RdmaNet_isRdmaAvailable0(JNIEnv *env, jclass cls) {
  57     return rdma_supported();
  58 }
  59 
  60 static int
  61 configureBlocking(int fd, jboolean blocking)
  62 {
  63     int flags = rfcntl(fd, F_GETFL);
  64     int newflags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
  65 
  66     return (flags == newflags) ? 0 : rfcntl(fd, F_SETFL, newflags);
  67 }
  68 
  69 JNIEXPORT void JNICALL
  70 Java_rdma_ch_RdmaNet_configureBlocking(JNIEnv *env, jclass clazz,
  71                                          jobject fdo, jboolean blocking)
  72 {
  73     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
  74     if (configureBlocking(fd, blocking) < 0)
  75         JNU_ThrowIOExceptionWithLastError(env, "Configure blocking failed");
  76 }
  77 
  78 JNIEXPORT void JNICALL
  79 Java_rdma_ch_RdmaNet_initIDs(JNIEnv *env, jclass clazz)
  80 {
  81     CHECK_NULL(clazz = (*env)->FindClass(env, "java/io/FileDescriptor"));
  82     CHECK_NULL(fd_fdID = (*env)->GetFieldID(env, clazz, "fd", "I"));
  83     initInetAddressIDs(env);
  84 }
  85 
  86 JNIEXPORT jboolean JNICALL
  87 Java_rdma_ch_RdmaNet_isIPv6Available0(JNIEnv* env, jclass cl)
  88 {
  89     return (ipv6_available()) ? JNI_TRUE : JNI_FALSE;
  90 }
  91 
  92 JNIEXPORT jboolean JNICALL
  93 Java_rdma_ch_RdmaNet_canIPv6SocketJoinIPv4Group0(JNIEnv* env, jclass cl)
  94 {
  95     return JNI_TRUE;
  96 }
  97 
  98 JNIEXPORT jboolean JNICALL
  99 Java_rdma_ch_RdmaNet_canJoin6WithIPv4Group0(JNIEnv* env, jclass cl)
 100 {
 101     return JNI_FALSE;
 102 }
 103 
 104 JNIEXPORT jint JNICALL
 105 Java_rdma_ch_RdmaNet_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6,
 106                             jboolean stream, jboolean reuse, jboolean ignored)
 107 {
 108     int fd;
 109     int type = (stream ? SOCK_STREAM : SOCK_DGRAM);
 110     int domain = (ipv6_available() && preferIPv6) ? AF_INET6 : AF_INET;
 111 
 112     fd = rsocket(domain, type, 0);
 113     if (fd < 0) {
 114         return handleSocketError(env, errno);
 115     }
 116 
 117     if (reuse) {
 118         int arg = 1;
 119         if (rsetsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg,
 120                        sizeof(arg)) < 0) {
 121             JNU_ThrowByNameWithLastError(env,
 122                                          JNU_JAVANETPKG "SocketException",
 123                                          "Unable to set SO_REUSEADDR");
 124             rclose(fd);
 125             return -1;
 126         }
 127     }
 128 
 129     return fd;
 130 }
 131 
 132 JNIEXPORT void JNICALL
 133 Java_rdma_ch_RdmaNet_bind0(JNIEnv *env, jclass clazz, jobject fdo, jboolean preferIPv6,
 134                           jboolean useExclBind, jobject iao, int port)
 135 {
 136     SOCKETADDRESS sa;
 137     int sa_len = 0;
 138     int rv = 0;
 139 
 140     if (NET_InetAddressToSockaddr(env, iao, port, &sa, &sa_len,
 141                                   preferIPv6) != 0) {
 142         return;
 143     }
 144 
 145     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 146     rv = RDMA_Bind(fd, &sa, sa_len);
 147     if (rv != 0) {
 148         handleSocketError(env, errno);
 149     }
 150 }
 151 
 152 JNIEXPORT void JNICALL
 153 Java_rdma_ch_RdmaNet_listen(JNIEnv *env, jclass cl, jobject fdo, jint backlog)
 154 {
 155     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 156     if (rlisten(fd, backlog) < 0)
 157        handleSocketError(env, errno);
 158 }
 159 
 160 JNIEXPORT jint JNICALL
 161 Java_rdma_ch_RdmaNet_connect0(JNIEnv *env, jclass clazz, jboolean preferIPv6,
 162                              jobject fdo, jobject iao, jint port)
 163 {
 164     SOCKETADDRESS sa;
 165     int sa_len = 0;
 166     int rv;
 167 
 168     if (NET_InetAddressToSockaddr(env, iao, port, &sa, &sa_len,
 169                                   preferIPv6) != 0) {
 170         return IOS_THROWN;
 171     }
 172 
 173     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 174 
 175     rv = rconnect(fd, &sa.sa, sa_len);
 176     if (rv != 0) {
 177         if (errno == EINPROGRESS) {
 178             return IOS_UNAVAILABLE;
 179         } else if (errno == EINTR) {
 180             return IOS_INTERRUPTED;
 181         }
 182         return handleSocketError(env, errno);
 183     }
 184     return 1;
 185 }
 186 
 187 JNIEXPORT jint JNICALL
 188 Java_rdma_ch_RdmaNet_localPort(JNIEnv *env, jclass clazz, jobject fdo)
 189 {
 190     SOCKETADDRESS sa;
 191     socklen_t sa_len = sizeof(SOCKETADDRESS);
 192     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 193     if (rgetsockname(fd, &sa.sa, &sa_len) < 0) {
 194         handleSocketError(env, errno);
 195         return -1;
 196     }
 197     return NET_GetPortFromSockaddr(&sa);
 198 }
 199 
 200 JNIEXPORT jobject JNICALL
 201 Java_rdma_ch_RdmaNet_localInetAddress(JNIEnv *env, jclass clazz, jobject fdo)
 202 {
 203     SOCKETADDRESS sa;
 204     socklen_t sa_len = sizeof(SOCKETADDRESS);
 205     int port;
 206     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 207     if (rgetsockname(fd, &sa.sa, &sa_len) < 0) {
 208         handleSocketError(env, errno);
 209         return NULL;
 210     }
 211     return NET_SockaddrToInetAddress(env, &sa, &port);
 212 }
 213 
 214 JNIEXPORT jint JNICALL
 215 Java_rdma_ch_RdmaNet_getIntOption0(JNIEnv *env, jclass clazz, jobject fdo,
 216                                    jboolean mayNeedConversion, jint level, jint opt)
 217 {
 218     int result;
 219     struct linger linger;
 220     u_char carg;
 221     void *arg;
 222     socklen_t arglen;
 223     int n;
 224 
 225     arg = (void *)&result;
 226     arglen = sizeof(result);
 227 
 228     if (level == SOL_SOCKET && opt == SO_LINGER) {
 229         arg = (void *)&linger;
 230         arglen = sizeof(linger);
 231     }
 232 
 233     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 234     if (mayNeedConversion) {
 235         n = RDMA_GetSockOpt(fd, level, opt, arg, (int*)&arglen);
 236     } else {
 237         n = rgetsockopt(fd, level, opt, arg, &arglen);
 238     }
 239     if (n < 0) {
 240         JNU_ThrowByNameWithLastError(env,
 241                                      JNU_JAVANETPKG "SocketException",
 242                                      "rdma.ch.RdmaNet.getIntOption");
 243         return -1;
 244     }
 245 
 246     if (level == SOL_SOCKET && opt == SO_LINGER)
 247         return linger.l_onoff ? (jint)linger.l_linger : (jint)-1;
 248 
 249     return (jint)result;
 250 }
 251 
 252 JNIEXPORT void JNICALL
 253 Java_rdma_ch_RdmaNet_setIntOption0(JNIEnv *env, jclass clazz, jobject fdo,
 254                                    jboolean mayNeedConversion, jint level,
 255                                    jint opt, jint arg, jboolean isIPv6)
 256 {
 257     int result;
 258     struct linger linger;
 259     u_char carg;
 260     void *parg;
 261     socklen_t arglen;
 262     int n;
 263 
 264     parg = (void*)&arg;
 265     arglen = sizeof(arg);
 266 
 267     if (level == SOL_SOCKET && opt == SO_LINGER) {
 268         parg = (void *)&linger;
 269         arglen = sizeof(linger);
 270         if (arg >= 0) {
 271             linger.l_onoff = 1;
 272             linger.l_linger = arg;
 273         } else {
 274             linger.l_onoff = 0;
 275             linger.l_linger = 0;
 276         }
 277     }
 278 
 279     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 280     if (mayNeedConversion) {
 281         n = RDMA_SetSockOpt(fd, level, opt, parg, arglen);
 282     } else {
 283         n = rsetsockopt(fd, level, opt, parg, arglen);
 284     }
 285     if (n < 0) {
 286         JNU_ThrowByNameWithLastError(env,
 287                                      JNU_JAVANETPKG "SocketException",
 288                                      "rdma.ch.RdmaNet.setIntOption");
 289     }
 290 }
 291 
 292 JNIEXPORT void JNICALL
 293 Java_rdma_ch_RdmaNet_shutdown(JNIEnv *env, jclass cl, jobject fdo, jint jhow)
 294 {
 295     int how = (jhow == sun_nio_ch_Net_SHUT_RD) ? SHUT_RD :
 296         (jhow == sun_nio_ch_Net_SHUT_WR) ? SHUT_WR : SHUT_RDWR;
 297     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 298     if ((rshutdown(fd, how) < 0) && (errno != ENOTCONN))
 299         handleSocketError(env, errno);
 300 }
 301 
 302 JNIEXPORT jint JNICALL
 303 Java_rdma_ch_RdmaNet_poll(JNIEnv* env, jclass this, jobject fdo, jint events, jlong timeout)
 304 {
 305     struct pollfd pfd;
 306     int rv;
 307     pfd.fd = (*env)->GetIntField(env, fdo, fd_fdID);
 308     pfd.events = events;
 309     if (timeout < -1) {
 310         timeout = -1;
 311     } else if (timeout > INT_MAX) {
 312         timeout = INT_MAX;
 313     }
 314     rv = rpoll(&pfd, 1, (int)timeout);
 315 
 316     if (rv >= 0) {
 317         return pfd.revents;
 318     } else if (errno == EINTR) {
 319         return IOS_INTERRUPTED;
 320     } else {
 321         handleSocketError(env, errno);
 322         return IOS_THROWN;
 323     }
 324 }