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 //#include "rdma_ch_RdmaPollArrayWrapper.h"
  46 
  47 #ifndef IP_MULTICAST_ALL
  48   #define IP_MULTICAST_ALL    49
  49 #endif
  50 
  51 #define COPY_INET6_ADDRESS(env, source, target) \
  52     (*env)->GetByteArrayRegion(env, source, 0, 16, target)
  53 
  54 static jfieldID fd_fdID;
  55 
  56 JNIEXPORT jboolean JNICALL
  57 Java_rdma_ch_RdmaNet_isRdmaAvailable0(JNIEnv *env, jclass cls) {
  58     return rdma_supported();
  59 }
  60 
  61 static int
  62 configureBlocking(int fd, jboolean blocking)
  63 {
  64     int flags = rfcntl(fd, F_GETFL);
  65     int newflags = blocking ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK);
  66 
  67     return (flags == newflags) ? 0 : rfcntl(fd, F_SETFL, newflags);
  68 }
  69 
  70 JNIEXPORT void JNICALL
  71 Java_rdma_ch_RdmaNet_configureBlocking(JNIEnv *env, jclass clazz,
  72                                          jobject fdo, jboolean blocking)
  73 {
  74     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
  75     if (configureBlocking(fd, blocking) < 0)
  76         JNU_ThrowIOExceptionWithLastError(env, "Configure blocking failed");
  77 }
  78 
  79 JNIEXPORT void JNICALL
  80 Java_rdma_ch_RdmaNet_initIDs(JNIEnv *env, jclass clazz)
  81 {
  82     CHECK_NULL(clazz = (*env)->FindClass(env, "java/io/FileDescriptor"));
  83     CHECK_NULL(fd_fdID = (*env)->GetFieldID(env, clazz, "fd", "I"));
  84     initInetAddressIDs(env);
  85 }
  86 
  87 JNIEXPORT jboolean JNICALL
  88 Java_rdma_ch_RdmaNet_isIPv6Available0(JNIEnv* env, jclass cl)
  89 {
  90     return (ipv6_available()) ? JNI_TRUE : JNI_FALSE;
  91 }
  92 
  93 JNIEXPORT jboolean JNICALL
  94 Java_rdma_ch_RdmaNet_canIPv6SocketJoinIPv4Group0(JNIEnv* env, jclass cl)
  95 {
  96     return JNI_TRUE;
  97 }
  98 
  99 JNIEXPORT jboolean JNICALL
 100 Java_rdma_ch_RdmaNet_canJoin6WithIPv4Group0(JNIEnv* env, jclass cl)
 101 {
 102     return JNI_FALSE;
 103 }
 104 
 105 JNIEXPORT jint JNICALL
 106 Java_rdma_ch_RdmaNet_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6,
 107                             jboolean stream, jboolean reuse, jboolean ignored)
 108 {
 109     int fd;
 110     int type = (stream ? SOCK_STREAM : SOCK_DGRAM);
 111     int domain = (ipv6_available() && preferIPv6) ? AF_INET6 : AF_INET;
 112 
 113     fd = rsocket(domain, type, 0);
 114     if (fd < 0) {
 115         return handleSocketError(env, errno);
 116     }
 117 
 118     /* Disable IPV6_V6ONLY to ensure dual-socket support */
 119     if (domain == AF_INET6) {
 120         int arg = 0;
 121         if (rsetsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg,
 122                        sizeof(int)) < 0) {
 123             JNU_ThrowByNameWithLastError(env,
 124                                          JNU_JAVANETPKG "SocketException",
 125                                          "Unable to set IPV6_V6ONLY");
 126             rclose(fd);
 127             return -1;
 128         }
 129     }
 130 
 131     if (reuse) {
 132         int arg = 1;
 133         if (rsetsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg,
 134                        sizeof(arg)) < 0) {
 135             JNU_ThrowByNameWithLastError(env,
 136                                          JNU_JAVANETPKG "SocketException",
 137                                          "Unable to set SO_REUSEADDR");
 138             rclose(fd);
 139             return -1;
 140         }
 141     }
 142 
 143     if (type == SOCK_DGRAM) {
 144         int arg = 0;
 145         int level = (domain == AF_INET6) ? IPPROTO_IPV6 : IPPROTO_IP;
 146         if ((rsetsockopt(fd, level, IP_MULTICAST_ALL, (char*)&arg, sizeof(arg)) < 0) &&
 147             (errno != ENOPROTOOPT)) {
 148             JNU_ThrowByNameWithLastError(env,
 149                                          JNU_JAVANETPKG "SocketException",
 150                                          "Unable to set IP_MULTICAST_ALL");
 151             rclose(fd);
 152             return -1;
 153         }
 154     }
 155 
 156     /* By default, Linux uses the route default */
 157     if (domain == AF_INET6 && type == SOCK_DGRAM) {
 158         int arg = 1;
 159         if (rsetsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &arg,
 160                        sizeof(arg)) < 0) {
 161             JNU_ThrowByNameWithLastError(env,
 162                                          JNU_JAVANETPKG "SocketException",
 163                                          "Unable to set IPV6_MULTICAST_HOPS");
 164             rclose(fd);
 165             return -1;
 166         }
 167     }
 168     return fd;
 169 }
 170 
 171 JNIEXPORT void JNICALL
 172 Java_rdma_ch_RdmaNet_bind0(JNIEnv *env, jclass clazz, jobject fdo, jboolean preferIPv6,
 173                           jboolean useExclBind, jobject iao, int port)
 174 {
 175     SOCKETADDRESS sa;
 176     int sa_len = 0;
 177     int rv = 0;
 178 
 179     if (NET_InetAddressToSockaddr(env, iao, port, &sa, &sa_len,
 180                                   preferIPv6) != 0) {
 181         return;
 182     }
 183 
 184     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 185     rv = RDMA_Bind(fd, &sa, sa_len);
 186     if (rv != 0) {
 187         handleSocketError(env, errno);
 188     }
 189 }
 190 
 191 JNIEXPORT void JNICALL
 192 Java_rdma_ch_RdmaNet_listen(JNIEnv *env, jclass cl, jobject fdo, jint backlog)
 193 {
 194     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 195     if (rlisten(fd, backlog) < 0)
 196        handleSocketError(env, errno);
 197 }
 198 
 199 JNIEXPORT jint JNICALL
 200 Java_rdma_ch_RdmaNet_connect0(JNIEnv *env, jclass clazz, jboolean preferIPv6,
 201                              jobject fdo, jobject iao, jint port)
 202 {
 203     SOCKETADDRESS sa;
 204     int sa_len = 0;
 205     int rv;
 206 
 207     if (NET_InetAddressToSockaddr(env, iao, port, &sa, &sa_len,
 208                                   preferIPv6) != 0) {
 209         return IOS_THROWN;
 210     }
 211 
 212     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 213     rv = rconnect(fd, &sa.sa, sa_len);
 214     if (rv != 0) {
 215         if (errno == EINPROGRESS) {
 216             return IOS_UNAVAILABLE;
 217         } else if (errno == EINTR) {
 218             return IOS_INTERRUPTED;
 219         }
 220         return handleSocketError(env, errno);
 221     }
 222     return 1;
 223 }
 224 
 225 JNIEXPORT jint JNICALL
 226 Java_rdma_ch_RdmaNet_localPort(JNIEnv *env, jclass clazz, jobject fdo)
 227 {
 228     SOCKETADDRESS sa;
 229     socklen_t sa_len = sizeof(SOCKETADDRESS);
 230     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 231     if (rgetsockname(fd, &sa.sa, &sa_len) < 0) {
 232         handleSocketError(env, errno);
 233         return -1;
 234     }
 235     return NET_GetPortFromSockaddr(&sa);
 236 }
 237 
 238 JNIEXPORT jobject JNICALL
 239 Java_rdma_ch_RdmaNet_localInetAddress(JNIEnv *env, jclass clazz, jobject fdo)
 240 {
 241     SOCKETADDRESS sa;
 242     socklen_t sa_len = sizeof(SOCKETADDRESS);
 243     int port;
 244     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 245     if (rgetsockname(fd, &sa.sa, &sa_len) < 0) {
 246         handleSocketError(env, errno);
 247         return NULL;
 248     }
 249     return NET_SockaddrToInetAddress(env, &sa, &port);
 250 }
 251 
 252 JNIEXPORT jint JNICALL
 253 Java_rdma_ch_RdmaNet_getIntOption0(JNIEnv *env, jclass clazz, jobject fdo,
 254                                    jboolean mayNeedConversion, jint level, jint opt)
 255 {
 256     int result;
 257     u_char carg;
 258     void *arg;
 259     socklen_t arglen;
 260     int n;
 261 
 262     arg = (void *)&result;
 263     arglen = sizeof(result);
 264 
 265     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 266     if (mayNeedConversion) {
 267         n = RDMA_GetSockOpt(fd, level, opt, arg, (int*)&arglen);
 268     } else {
 269         n = rgetsockopt(fd, level, opt, arg, &arglen);
 270     }
 271     if (n < 0) {
 272         JNU_ThrowByNameWithLastError(env,
 273                                      JNU_JAVANETPKG "SocketException",
 274                                      "rdma.ch.RdmaNet.getIntOption");
 275         return -1;
 276     }
 277 
 278     return (jint)result;
 279 }
 280 
 281 JNIEXPORT void JNICALL
 282 Java_rdma_ch_RdmaNet_setIntOption0(JNIEnv *env, jclass clazz, jobject fdo,
 283                                    jboolean mayNeedConversion, jint level,
 284                                    jint opt, jint arg, jboolean isIPv6)
 285 {
 286     int result;
 287     u_char carg;
 288     void *parg;
 289     socklen_t arglen;
 290     int n;
 291 
 292     parg = (void*)&arg;
 293     arglen = sizeof(arg);
 294 
 295     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 296     if (mayNeedConversion) {
 297         n = RDMA_SetSockOpt(fd, level, opt, parg, arglen);
 298     } else {
 299         n = rsetsockopt(fd, level, opt, parg, arglen);
 300     }
 301     if (n < 0) {
 302         JNU_ThrowByNameWithLastError(env,
 303                                      JNU_JAVANETPKG "SocketException",
 304                                      "rdma.ch.RdmaNet.setIntOption");
 305     }
 306 }
 307 
 308 JNIEXPORT void JNICALL
 309 Java_rdma_ch_RdmaNet_shutdown(JNIEnv *env, jclass cl, jobject fdo, jint jhow)
 310 {
 311     int how = (jhow == sun_nio_ch_Net_SHUT_RD) ? SHUT_RD :
 312         (jhow == sun_nio_ch_Net_SHUT_WR) ? SHUT_WR : SHUT_RDWR;
 313     int fd = (*env)->GetIntField(env, fdo, fd_fdID);
 314     if ((rshutdown(fd, how) < 0) && (errno != ENOTCONN))
 315         handleSocketError(env, errno);
 316 }
 317 
 318 JNIEXPORT jint JNICALL
 319 Java_rdma_ch_RdmaNet_poll(JNIEnv* env, jclass this, jobject fdo, jint events, jlong timeout)
 320 {
 321     struct pollfd pfd;
 322     int rv;
 323     pfd.fd = (*env)->GetIntField(env, fdo, fd_fdID);
 324     pfd.events = events;
 325     if (timeout < -1) {
 326         timeout = -1;
 327     } else if (timeout > INT_MAX) {
 328         timeout = INT_MAX;
 329     }
 330     rv = rpoll(&pfd, 1, (int)timeout);
 331 
 332     if (rv >= 0) {
 333         return pfd.revents;
 334     } else if (errno == EINTR) {
 335         return IOS_INTERRUPTED;
 336     } else {
 337         handleSocketError(env, errno);
 338         return IOS_THROWN;
 339     }
 340 }
 341 
 342 /*
 343 JNIEXPORT jshort JNICALL
 344 Java_rdma_ch_RdmaNet_pollinValue(JNIEnv *env, jclass this)
 345 {
 346     return (jshort)POLLIN;
 347 }
 348 
 349 JNIEXPORT jshort JNICALL
 350 Java_rdma_ch_RdmaNet_polloutValue(JNIEnv *env, jclass this)
 351 {
 352     return (jshort)POLLOUT;
 353 }
 354 
 355 JNIEXPORT jshort JNICALL
 356 Java_rdma_ch_RdmaNet_pollerrValue(JNIEnv *env, jclass this)
 357 {
 358     return (jshort)POLLERR;
 359 }
 360 
 361 JNIEXPORT jshort JNICALL
 362 Java_rdma_ch_RdmaNet_pollhupValue(JNIEnv *env, jclass this)
 363 {
 364     return (jshort)POLLHUP;
 365 }
 366 
 367 JNIEXPORT jshort JNICALL
 368 Java_rdma_ch_RdmaNet_pollnvalValue(JNIEnv *env, jclass this)
 369 {
 370     return (jshort)POLLNVAL;
 371 }
 372 
 373 JNIEXPORT jshort JNICALL
 374 Java_rdma_ch_RdmaNet_pollconnValue(JNIEnv *env, jclass this)
 375 {
 376     return (jshort)POLLOUT;
 377 }
 378 */