1 /*
   2  * Copyright (c) 1997, 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 #include <errno.h>
  26 
  27 #include "jvm.h"
  28 #include "rdma_util_md.h"
  29 
  30 #include "java_net_SocketOptions.h"
  31 
  32 #include "net_util.h"
  33 #include "net_util_md.h"
  34 #include "rdma_ch_LinuxRdmaSocketImpl.h"
  35 
  36 /************************************************************************
  37  * RdmaSocketImpl
  38  */
  39 
  40 static jfieldID IO_fd_fdID;
  41 
  42 jfieldID psi_fdID;
  43 jfieldID psi_addressID;
  44 jfieldID psi_ipaddressID;
  45 jfieldID psi_portID;
  46 jfieldID psi_localportID;
  47 jfieldID psi_timeoutID;
  48 jfieldID psi_trafficClassID;
  49 jfieldID psi_serverSocketID;
  50 jfieldID psi_fdLockID;
  51 jfieldID psi_closePendingID;
  52 
  53 
  54 /*
  55  * file descriptor used for dup2
  56  */
  57 static int marker_fd = -1;
  58 
  59 #define SET_NONBLOCKING(fd) {           \
  60         int flags = rfcntl(fd, F_GETFL); \
  61         flags |= O_NONBLOCK;            \
  62         rfcntl(fd, F_SETFL, flags);      \
  63 }
  64 
  65 #define SET_BLOCKING(fd) {              \
  66         int flags = rfcntl(fd, F_GETFL); \
  67         flags &= ~O_NONBLOCK;           \
  68         rfcntl(fd, F_SETFL, flags);      \
  69 }
  70 
  71 static jclass socketExceptionCls;
  72 
  73 static int getMarkerFD()
  74 {
  75     int sv[2];
  76 
  77 #ifdef AF_UNIX
  78     if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
  79         return -1;
  80     }
  81 #else
  82     return -1;
  83 #endif
  84 
  85     rshutdown(sv[0], 2);
  86     rclose(sv[1]);
  87 
  88     return sv[0];
  89 }
  90 
  91 static int getFD(JNIEnv *env, jobject this) {
  92     jobject fdObj = (*env)->GetObjectField(env, this, psi_fdID);
  93     CHECK_NULL_RETURN(fdObj, -1);
  94     return (*env)->GetIntField(env, fdObj, IO_fd_fdID);
  95 }
  96 
  97 
  98 jfieldID
  99 NET_GetFileDescriptorID(JNIEnv *env)
 100 {
 101     jclass cls = (*env)->FindClass(env, "java/io/FileDescriptor");
 102     CHECK_NULL_RETURN(cls, NULL);
 103     return (*env)->GetFieldID(env, cls, "fd", "I");
 104 }
 105 
 106 JNIEXPORT void JNICALL
 107 Java_rdma_ch_LinuxRdmaSocketImpl_initProto(JNIEnv *env, jclass cls) {
 108     jclass clazz = (*env)->FindClass(env, "rdma/ch/RdmaSocketImpl");
 109     psi_fdID = (*env)->GetFieldID(env, clazz , "fd",
 110                                   "Ljava/io/FileDescriptor;");
 111     CHECK_NULL(psi_fdID);
 112     psi_addressID = (*env)->GetFieldID(env, clazz, "address",
 113                                           "Ljava/net/InetAddress;");
 114     CHECK_NULL(psi_addressID);
 115     psi_timeoutID = (*env)->GetFieldID(env, clazz, "timeout", "I");
 116     CHECK_NULL(psi_timeoutID);
 117     psi_trafficClassID = (*env)->GetFieldID(env, clazz, "trafficClass", "I");
 118     CHECK_NULL(psi_trafficClassID);
 119     psi_portID = (*env)->GetFieldID(env, clazz, "port", "I");
 120     CHECK_NULL(psi_portID);
 121     psi_localportID = (*env)->GetFieldID(env, clazz, "localport", "I");
 122     CHECK_NULL(psi_localportID);
 123     psi_serverSocketID = (*env)->GetFieldID(env, clazz, "serverSocket",
 124                         "Ljava/net/ServerSocket;");
 125     CHECK_NULL(psi_serverSocketID);
 126 
 127     IO_fd_fdID = NET_GetFileDescriptorID(env);
 128     CHECK_NULL(IO_fd_fdID);
 129 
 130     initInetAddressIDs(env);
 131     JNU_CHECK_EXCEPTION(env);
 132     marker_fd = getMarkerFD();
 133 }
 134 
 135 JNIEXPORT jboolean JNICALL
 136 Java_rdma_ch_LinuxRdmaSocketImpl_isRdmaAvailable0(JNIEnv *env, jclass cls) {
 137     return rdma_supported();
 138 }
 139 
 140 void
 141 NET_SetTrafficClass(SOCKETADDRESS *sa, int trafficClass) {
 142     if (sa->sa.sa_family == AF_INET6) {
 143         sa->sa6.sin6_flowinfo = htonl((trafficClass & 0xff) << 20);
 144     }
 145 }
 146 
 147 /*
 148  * Class:     jdk_net_LinuxRdmaSocketImpl
 149  * Method:    rdmaSocketCreate
 150  * Signature: (Z)V */
 151 JNIEXPORT void JNICALL
 152 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketCreate(JNIEnv *env, jobject this,
 153                                            jboolean stream, jobject impl) {
 154     jobject fdObj, ssObj;
 155     int fd;
 156     int type = (stream ? SOCK_STREAM : SOCK_DGRAM);
 157     int domain = ipv6_available() ? AF_INET6 : AF_INET;
 158 
 159     if (socketExceptionCls == NULL) {
 160         jclass c = (*env)->FindClass(env, "java/net/SocketException");
 161         CHECK_NULL(c);
 162         socketExceptionCls = (jclass)(*env)->NewGlobalRef(env, c);
 163         CHECK_NULL(socketExceptionCls);
 164     }
 165 
 166     fdObj = (*env)->GetObjectField(env, impl, psi_fdID);
 167 
 168     if (fdObj == NULL) {
 169         (*env)->ThrowNew(env, socketExceptionCls, "null fd object");
 170         return;
 171     }
 172 
 173     if ((fd = rsocket(domain, type, 0)) == -1) {
 174         NET_ThrowNew(env, errno, "can't create socket");
 175         return;
 176     }
 177     if (domain == AF_INET6) {
 178         int arg = 0;
 179         if (rsetsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg,
 180                        sizeof(int)) < 0) {
 181             NET_ThrowNew(env, errno, "cannot set IPPROTO_IPV6");
 182             rclose(fd);
 183             return;
 184         }
 185     }
 186 
 187     ssObj = (*env)->GetObjectField(env, impl, psi_serverSocketID);
 188     if (ssObj != NULL) {
 189         int arg = 1;
 190         SET_NONBLOCKING(fd);
 191         if (RDMA_SetSockOpt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg,
 192                        sizeof(arg)) < 0) {
 193             NET_ThrowNew(env, errno, "cannot set SO_REUSEADDR");
 194             rclose(fd);
 195             return;
 196         }
 197     }
 198     (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd);
 199 }
 200 
 201 JNIEXPORT void JNICALL
 202 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketConnect(JNIEnv *env, jobject this, jobject impl,
 203                                                jobject iaObj, jint port,
 204                                                jint timeout)
 205 {
 206     jint localport = (*env)->GetIntField(env, impl, psi_localportID);
 207     int len = 0;
 208     jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID);
 209 
 210     jobject fdLock;
 211 
 212     jint trafficClass = (*env)->GetIntField(env, impl, psi_trafficClassID);
 213 
 214     jint fd;
 215 
 216     SOCKETADDRESS sa;
 217     int connect_rv = -1;
 218 
 219     if (IS_NULL(fdObj)) {
 220         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 221         return;
 222     } else {
 223         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 224     }
 225     if (IS_NULL(iaObj)) {
 226         JNU_ThrowNullPointerException(env, "inet address argument null.");
 227         return;
 228     }
 229 
 230     if (NET_InetAddressToSockaddr(env, iaObj, port, &sa, &len,
 231                                  JNI_TRUE) != 0) {
 232         return;
 233     }
 234 
 235     if (trafficClass != 0 && ipv6_available()) {
 236         NET_SetTrafficClass(&sa, trafficClass);
 237     }
 238 
 239     if (timeout <= 0) {
 240         connect_rv = RDMA_Connect(fd, &sa.sa, len);
 241     } else {
 242         SET_NONBLOCKING(fd);
 243 
 244         connect_rv = rconnect(fd, &sa.sa, len);
 245 
 246         if (connect_rv != 0) {
 247             socklen_t optlen;
 248             jlong nanoTimeout = (jlong) timeout * NET_NSEC_PER_MSEC;
 249             jlong prevNanoTime = JVM_NanoTime(env, 0);
 250 
 251             if (errno != EINPROGRESS) {
 252                 JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "ConnectException",
 253                              "connect failed");
 254                 SET_BLOCKING(fd);
 255                 return;
 256             }
 257 
 258             while (1) {
 259                 jlong newNanoTime;
 260                 struct pollfd pfd;
 261                 pfd.fd = fd;
 262                 pfd.events = POLLOUT;
 263 
 264                 errno = 0;
 265 
 266                 connect_rv = RDMA_Poll(&pfd, 1, nanoTimeout / NET_NSEC_PER_MSEC);
 267 
 268                 if (connect_rv >= 0) {
 269                     break;
 270                 }
 271                 if (errno != EINTR) {
 272                     break;
 273                 }
 274 
 275                 newNanoTime = JVM_NanoTime(env, 0);
 276                 nanoTimeout -= (newNanoTime - prevNanoTime);
 277                 if (nanoTimeout < NET_NSEC_PER_MSEC) {
 278                     connect_rv = 0;
 279                     break;
 280                 }
 281                 prevNanoTime = newNanoTime;
 282 
 283             }
 284 
 285             if (connect_rv == 0) {
 286                 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
 287                             "connect timed out");
 288 
 289                 SET_BLOCKING(fd);
 290                 rshutdown(fd, 2);
 291                 return;
 292             }
 293 
 294             optlen = sizeof(connect_rv);
 295             if (rgetsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv,
 296                             &optlen) <0) {
 297                 connect_rv = errno;
 298             }
 299         }
 300 
 301         SET_BLOCKING(fd);
 302 
 303         if (connect_rv != 0) {
 304             errno = connect_rv;
 305             connect_rv = -1;
 306         }
 307     }
 308 
 309     if (connect_rv < 0) {
 310         if (connect_rv == -1 && errno == EINVAL) {
 311             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 312                 "Invalid argument or cannot assign requested address");
 313             return;
 314         }
 315 #if defined(EPROTO)
 316         if (errno == EPROTO) {
 317             JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "ProtocolException",
 318                            "Protocol error");
 319             return;
 320         }
 321 #endif
 322         if (errno == ECONNREFUSED) {
 323             JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "ConnectException",
 324                            "Connection refused");
 325         } else if (errno == ETIMEDOUT) {
 326             JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "ConnectException",
 327                            "Connection timed out");
 328         } else if (errno == EHOSTUNREACH) {
 329             JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "NoRouteToHostException",
 330                            "Host unreachable");
 331         } else if (errno == EADDRNOTAVAIL) {
 332             JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "NoRouteToHostException",
 333                              "Address not available");
 334         } else if ((errno == EISCONN) || (errno == EBADF)) {
 335             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 336                             "Socket closed");
 337         } else {
 338             JNU_ThrowByNameWithMessageAndLastError
 339                 (env, JNU_JAVANETPKG "SocketException", "connect failed");
 340         }
 341         return;
 342     }
 343 
 344     (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd);
 345 
 346     (*env)->SetObjectField(env, impl, psi_addressID, iaObj);
 347     (*env)->SetIntField(env, impl, psi_portID, port);
 348 
 349     if (localport == 0) {
 350         socklen_t slen = sizeof(SOCKETADDRESS);
 351         if (rgetsockname(fd, &sa.sa, &slen) == -1) {
 352             JNU_ThrowByNameWithMessageAndLastError
 353                 (env, JNU_JAVANETPKG "SocketException", "Error getting socket name");
 354         } else {
 355             localport = NET_GetPortFromSockaddr(&sa);
 356             (*env)->SetIntField(env, impl, psi_localportID, localport);
 357         }
 358     }
 359 }
 360 
 361 JNIEXPORT void JNICALL
 362 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketBind(JNIEnv *env, jobject this, jobject impl,
 363                                              jobject iaObj, jint localport) {
 364     jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID);
 365     int fd;
 366     int len = 0;
 367     SOCKETADDRESS sa;
 368     
 369     if (IS_NULL(fdObj)) {
 370         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 371                         "Socket closed");
 372         return;
 373     } else {
 374         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 375     }
 376     if (IS_NULL(iaObj)) {
 377         JNU_ThrowNullPointerException(env, "iaObj is null.");
 378         return;
 379     }
 380 
 381     if (NET_InetAddressToSockaddr(env, iaObj, localport, &sa,
 382                                   &len, JNI_TRUE) != 0) {
 383         return;
 384     }
 385 
 386     if (RDMA_Bind(fd, &sa, len) < 0) {
 387         if (errno == EADDRINUSE || errno == EADDRNOTAVAIL ||
 388             errno == EPERM || errno == EACCES) {
 389             JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "BindException",
 390                            "Bind failed");
 391         } else {
 392             JNU_ThrowByNameWithMessageAndLastError
 393                 (env, JNU_JAVANETPKG "SocketException", "Bind failed");
 394         }
 395         return;
 396     }
 397     (*env)->SetObjectField(env, impl, psi_addressID, iaObj);
 398 
 399     if (localport == 0) {
 400         socklen_t slen = sizeof(SOCKETADDRESS);
 401         if (rgetsockname(fd, &sa.sa, &slen) == -1) {
 402             JNU_ThrowByNameWithMessageAndLastError
 403                 (env, JNU_JAVANETPKG "SocketException", "Error getting socket name");
 404             return;
 405         }
 406         localport = NET_GetPortFromSockaddr(&sa);
 407         (*env)->SetIntField(env, impl, psi_localportID, localport);
 408     } else {
 409         (*env)->SetIntField(env, impl, psi_localportID, localport);
 410     }
 411 }
 412 
 413 JNIEXPORT void JNICALL
 414 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketListen(JNIEnv *env, jobject this, jobject impl,
 415                                                jint count)
 416 {
 417     jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID);
 418     int fd;
 419 
 420     if (IS_NULL(fdObj)) {
 421         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 422                         "Socket closed");
 423         return;
 424     } else {
 425         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 426     }
 427 
 428     if (count == 0x7fffffff)
 429         count -= 1;
 430 
 431     int rv = rlisten(fd, count);
 432     if (rv == -1) {
 433         JNU_ThrowByNameWithMessageAndLastError
 434             (env, JNU_JAVANETPKG "SocketException", "Listen failed");
 435     }
 436 }
 437 
 438 JNIEXPORT void JNICALL
 439 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketAccept(JNIEnv *env, jobject this, jobject socket, jobject impl)
 440 {
 441     int port;
 442     jint timeout = (*env)->GetIntField(env, impl, psi_timeoutID);
 443     jlong prevNanoTime = 0;
 444     jlong nanoTimeout = (jlong) timeout * NET_NSEC_PER_MSEC;
 445     jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID);
 446 
 447     jobject socketFdObj;
 448     jobject socketAddressObj;
 449 
 450     jint fd;
 451 
 452     jint newfd;
 453 
 454     SOCKETADDRESS sa;
 455     socklen_t slen = sizeof(SOCKETADDRESS);
 456 
 457     if (IS_NULL(fdObj)) {
 458         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 459                         "Socket closed");
 460         return;
 461     } else {
 462         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 463     }
 464     if (IS_NULL(socket)) {
 465         JNU_ThrowNullPointerException(env, "socket is null");
 466         return;
 467     }
 468 
 469     for (;;) {
 470         int ret;
 471         jlong currNanoTime;
 472 
 473         if (prevNanoTime == 0 && nanoTimeout > 0) {
 474             prevNanoTime = JVM_NanoTime(env, 0);
 475         }
 476 
 477         if (timeout <= 0) {
 478             ret = RDMA_Timeout(env, fd, -1, 0);
 479         } else {
 480             ret = RDMA_Timeout(env, fd, nanoTimeout / NET_NSEC_PER_MSEC, prevNanoTime);
 481         }
 482 
 483         if (ret == 0) {
 484             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
 485                             "Accept timed out");
 486             return;
 487         } else if (ret == -1) {
 488             if (errno == EBADF) {
 489                JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 490             } else if (errno == ENOMEM) {
 491                JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed");
 492             } else {
 493                JNU_ThrowByNameWithMessageAndLastError
 494                    (env, JNU_JAVANETPKG "SocketException", "Accept failed");
 495             }
 496             return;
 497         }
 498 
 499         newfd = RDMA_Accept(fd, &sa.sa, &slen);
 500 
 501         if (newfd >= 0) {
 502             SET_BLOCKING(newfd);
 503             break;
 504         }
 505 
 506         if (!(errno == ECONNABORTED || errno == EWOULDBLOCK)) {
 507             break;
 508         }
 509 
 510         if (nanoTimeout >= NET_NSEC_PER_MSEC) {
 511             currNanoTime = JVM_NanoTime(env, 0);
 512             nanoTimeout -= (currNanoTime - prevNanoTime);
 513             if (nanoTimeout < NET_NSEC_PER_MSEC) {
 514                 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
 515                         "Accept timed out");
 516                 return;
 517             }
 518             prevNanoTime = currNanoTime;
 519         }
 520     }
 521 
 522     if (newfd < 0) {
 523         if (newfd == -2) {
 524             JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
 525                             "operation interrupted");
 526         } else {
 527             if (errno == EINVAL) {
 528                 errno = EBADF;
 529             }
 530             if (errno == EBADF) {
 531                 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 532             } else {
 533                 JNU_ThrowByNameWithMessageAndLastError
 534                     (env, JNU_JAVANETPKG "SocketException", "Accept failed");
 535             }
 536         }
 537         return;
 538     }
 539 
 540     socketAddressObj = NET_SockaddrToInetAddress(env, &sa, &port);
 541     if (socketAddressObj == NULL) {
 542         rclose(newfd);
 543         return;
 544     }
 545 
 546     socketFdObj = (*env)->GetObjectField(env, socket, psi_fdID);
 547     (*env)->SetIntField(env, socketFdObj, IO_fd_fdID, newfd);
 548     (*env)->SetObjectField(env, socket, psi_addressID, socketAddressObj);
 549     (*env)->SetIntField(env, socket, psi_portID, port);
 550      port = (*env)->GetIntField(env, impl, psi_localportID);
 551     (*env)->SetIntField(env, socket, psi_localportID, port);
 552 }
 553 
 554 
 555 JNIEXPORT jint JNICALL
 556 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketAvailable(JNIEnv *env, jobject this, jobject impl) {
 557     jint ret = -1;
 558     jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID);
 559     jint fd;
 560 
 561     if (IS_NULL(fdObj)) {
 562         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 563                         "Socket closed");
 564         return -1;
 565     } else {
 566         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 567     }
 568     if (RDMA_SocketAvailable(fd, &ret) == 0){
 569         if (errno == ECONNRESET) {
 570             JNU_ThrowByName(env, "sun/net/ConnectionResetException", "");
 571         } else {
 572             JNU_ThrowByNameWithMessageAndLastError
 573                 (env, JNU_JAVANETPKG "SocketException", "ioctl FIONREAD failed");
 574         }
 575     }
 576     return ret;
 577 }
 578 
 579 JNIEXPORT void JNICALL
 580 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketClose(JNIEnv *env, jobject this,
 581                                               jboolean useDeferredClose, jobject impl) {
 582     jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID);
 583     jint fd;
 584 
 585     if (IS_NULL(fdObj)) {
 586         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 587                         "socket already closed");
 588         return;
 589     } else {
 590         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 591     }
 592     if (fd != -1) {
 593         if (useDeferredClose && marker_fd >= 0) {
 594             RDMA_Dup2(marker_fd, fd);
 595         } else {
 596             (*env)->SetIntField(env, fdObj, IO_fd_fdID, -1);
 597             RDMA_SocketClose(fd);
 598         }
 599     }
 600 }
 601 
 602 JNIEXPORT void JNICALL
 603 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketShutdown(JNIEnv *env, jobject this,
 604                                                 jint howto, jobject impl)
 605 {
 606     jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID);
 607     jint fd;
 608 
 609     if (IS_NULL(fdObj)) {
 610         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 611                         "socket already closed");
 612         return;
 613     } else {
 614         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 615     }
 616     rshutdown(fd, howto);
 617 }
 618 
 619 
 620 JNIEXPORT void JNICALL
 621 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketSetOption
 622   (JNIEnv *env, jobject this, jobject impl, jint cmd, jboolean on, jobject value)
 623 {
 624     int fd;
 625     int level, optname, optlen;
 626     union {
 627         int i;
 628         struct linger ling;
 629     } optval;
 630 
 631     fd = getFD(env, impl);
 632     if (fd < 0) {
 633         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 634                         "Socket closed");
 635         return;
 636     }
 637 
 638     if (cmd == java_net_SocketOptions_SO_TIMEOUT) {
 639         return;
 640     }
 641 
 642     if (RDMA_MapSocketOption(cmd, &level, &optname)) {
 643         JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");
 644         return;
 645     }
 646 
 647     switch (cmd) {
 648         case java_net_SocketOptions_SO_SNDBUF :
 649         case java_net_SocketOptions_SO_RCVBUF :
 650         case java_net_SocketOptions_SO_LINGER :
 651             {
 652                 jclass cls;
 653                 jfieldID fid;
 654 
 655                 cls = (*env)->FindClass(env, "java/lang/Integer");
 656                 CHECK_NULL(cls);
 657                 fid = (*env)->GetFieldID(env, cls, "value", "I");
 658                 CHECK_NULL(fid);
 659 
 660                 if (cmd == java_net_SocketOptions_SO_LINGER) {
 661                     if (on) {
 662                         optval.ling.l_onoff = 1;
 663                         optval.ling.l_linger = (*env)->GetIntField(env, value, fid);
 664                     } else {
 665                         optval.ling.l_onoff = 0;
 666                         optval.ling.l_linger = 0;
 667                     }
 668                     optlen = sizeof(optval.ling);
 669                 } else {
 670                     optval.i = (*env)->GetIntField(env, value, fid);
 671                     optlen = sizeof(optval.i);
 672                 }
 673 
 674                 break;
 675             }
 676 
 677         default :
 678             optval.i = (on ? 1 : 0);
 679             optlen = sizeof(optval.i);
 680 
 681     }
 682     if (RDMA_SetSockOpt(fd, level, optname, (const void *)&optval, optlen) < 0) {
 683         JNU_ThrowByNameWithMessageAndLastError
 684             (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
 685     }
 686 }
 687 
 688 JNIEXPORT jint JNICALL
 689 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketGetOption
 690   (JNIEnv *env, jobject this, jobject impl, jint cmd, jobject iaContainerObj)
 691 {
 692     int fd;
 693     int level, optname, optlen;
 694     union {
 695         int i;
 696         struct linger ling;
 697     } optval;
 698 
 699     fd = getFD(env, impl);
 700     if (fd < 0) {
 701         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 702                         "Socket closed");
 703         return -1;
 704     }
 705 
 706     if (cmd == java_net_SocketOptions_SO_BINDADDR) {
 707         SOCKETADDRESS sa;
 708         socklen_t len = sizeof(SOCKETADDRESS);
 709         int port;
 710         jobject iaObj;
 711         jclass iaCntrClass;
 712         jfieldID iaFieldID;
 713 
 714         if (rgetsockname(fd, &sa.sa, &len) < 0) {
 715             JNU_ThrowByNameWithMessageAndLastError
 716                 (env, JNU_JAVANETPKG "SocketException", "Error getting socket name");
 717             return -1;
 718         }
 719         iaObj = NET_SockaddrToInetAddress(env, &sa, &port);
 720         CHECK_NULL_RETURN(iaObj, -1);
 721 
 722         iaCntrClass = (*env)->GetObjectClass(env, iaContainerObj);
 723         iaFieldID = (*env)->GetFieldID(env, iaCntrClass, "addr", "Ljava/net/InetAddress;");
 724         CHECK_NULL_RETURN(iaFieldID, -1);
 725         (*env)->SetObjectField(env, iaContainerObj, iaFieldID, iaObj);
 726         return 0; 
 727     }
 728 
 729     if (RDMA_MapSocketOption(cmd, &level, &optname)) {
 730         JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");
 731         return -1;
 732     }
 733 
 734     /*
 735      * Args are int except for SO_LINGER
 736      */
 737     if (cmd == java_net_SocketOptions_SO_LINGER) {
 738         optlen = sizeof(optval.ling);
 739     } else {
 740         optlen = sizeof(optval.i);
 741     }
 742 
 743     if (RDMA_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) {
 744         JNU_ThrowByNameWithMessageAndLastError
 745             (env, JNU_JAVANETPKG "SocketException", "Error getting socket option");
 746         return -1;
 747     }
 748 
 749     switch (cmd) {
 750         case java_net_SocketOptions_SO_LINGER:
 751             return (optval.ling.l_onoff ? optval.ling.l_linger: -1);
 752 
 753         case java_net_SocketOptions_SO_SNDBUF:
 754         case java_net_SocketOptions_SO_RCVBUF:
 755             return optval.i;
 756 
 757         default :
 758             return (optval.i == 0) ? -1 : 1;
 759     }
 760 }
 761 
 762 JNIEXPORT void JNICALL
 763 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketSendUrgentData(JNIEnv *env, jobject this, jobject impl,
 764                                              jint data) {
 765     jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID);
 766     int n, fd;
 767     unsigned char d = data & 0xFF;
 768 
 769     if (IS_NULL(fdObj)) {
 770         JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");
 771         return;
 772     } else {
 773         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 774         if (fd == -1) {
 775             JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");
 776             return;
 777         }
 778 
 779     }
 780     n = RDMA_Send(fd, (char *)&d, 1, MSG_OOB);
 781     if (n == -1) {
 782         JNU_ThrowIOExceptionWithLastError(env, "Write failed");
 783     }
 784 }