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                 connect_rv = RDMA_Poll(&pfd, 1, nanoTimeout / NET_NSEC_PER_MSEC);
 266 
 267                 if (connect_rv >= 0) {
 268                     break;
 269                 }
 270                 if (errno != EINTR) {
 271                     break;
 272                 }
 273 
 274                 newNanoTime = JVM_NanoTime(env, 0);
 275                 nanoTimeout -= (newNanoTime - prevNanoTime);
 276                 if (nanoTimeout < NET_NSEC_PER_MSEC) {
 277                     connect_rv = 0;
 278                     break;
 279                 }
 280                 prevNanoTime = newNanoTime;
 281 
 282             }
 283 
 284             if (connect_rv == 0) {
 285                 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
 286                             "connect timed out");
 287 
 288                 SET_BLOCKING(fd);
 289                 rshutdown(fd, 2);
 290                 return;
 291             }
 292 
 293             optlen = sizeof(connect_rv);
 294             if (rgetsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&connect_rv,
 295                             &optlen) <0) {
 296                 connect_rv = errno;
 297             }
 298         }
 299 
 300         SET_BLOCKING(fd);
 301 
 302         if (connect_rv != 0) {
 303             errno = connect_rv;
 304             connect_rv = -1;
 305         }
 306     }
 307 
 308     if (connect_rv < 0) {
 309         if (connect_rv == -1 && errno == EINVAL) {
 310             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 311                 "Invalid argument or cannot assign requested address");
 312             return;
 313         }
 314 #if defined(EPROTO)
 315         if (errno == EPROTO) {
 316             JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "ProtocolException",
 317                            "Protocol error");
 318             return;
 319         }
 320 #endif
 321         if (errno == ECONNREFUSED) {
 322             JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "ConnectException",
 323                            "Connection refused");
 324         } else if (errno == ETIMEDOUT) {
 325             JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "ConnectException",
 326                            "Connection timed out");
 327         } else if (errno == EHOSTUNREACH) {
 328             JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "NoRouteToHostException",
 329                            "Host unreachable");
 330         } else if (errno == EADDRNOTAVAIL) {
 331             JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "NoRouteToHostException",
 332                              "Address not available");
 333         } else if ((errno == EISCONN) || (errno == EBADF)) {
 334             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 335                             "Socket closed");
 336         } else {
 337             JNU_ThrowByNameWithMessageAndLastError
 338                 (env, JNU_JAVANETPKG "SocketException", "connect failed");
 339         }
 340         return;
 341     }
 342 
 343     (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd);
 344 
 345     (*env)->SetObjectField(env, impl, psi_addressID, iaObj);
 346     (*env)->SetIntField(env, impl, psi_portID, port);
 347 
 348     if (localport == 0) {
 349         socklen_t slen = sizeof(SOCKETADDRESS);
 350         if (rgetsockname(fd, &sa.sa, &slen) == -1) {
 351             JNU_ThrowByNameWithMessageAndLastError
 352                 (env, JNU_JAVANETPKG "SocketException", "Error getting socket name");
 353         } else {
 354             localport = NET_GetPortFromSockaddr(&sa);
 355             (*env)->SetIntField(env, impl, psi_localportID, localport);
 356         }
 357     }
 358 }
 359 
 360 JNIEXPORT void JNICALL
 361 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketBind(JNIEnv *env, jobject this, jobject impl,
 362                                              jobject iaObj, jint localport) {
 363     jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID);
 364     int fd;
 365     int len = 0;
 366     SOCKETADDRESS sa;
 367     
 368     if (IS_NULL(fdObj)) {
 369         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 370                         "Socket closed");
 371         return;
 372     } else {
 373         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 374     }
 375     if (IS_NULL(iaObj)) {
 376         JNU_ThrowNullPointerException(env, "iaObj is null.");
 377         return;
 378     }
 379 
 380     if (NET_InetAddressToSockaddr(env, iaObj, localport, &sa,
 381                                   &len, JNI_TRUE) != 0) {
 382         return;
 383     }
 384 
 385     if (RDMA_Bind(fd, &sa, len) < 0) {
 386         if (errno == EADDRINUSE || errno == EADDRNOTAVAIL ||
 387             errno == EPERM || errno == EACCES) {
 388             JNU_ThrowByNameWithMessageAndLastError(env, JNU_JAVANETPKG "BindException",
 389                            "Bind failed");
 390         } else {
 391             JNU_ThrowByNameWithMessageAndLastError
 392                 (env, JNU_JAVANETPKG "SocketException", "Bind failed");
 393         }
 394         return;
 395     }
 396     (*env)->SetObjectField(env, impl, psi_addressID, iaObj);
 397 
 398     if (localport == 0) {
 399         socklen_t slen = sizeof(SOCKETADDRESS);
 400         if (rgetsockname(fd, &sa.sa, &slen) == -1) {
 401             JNU_ThrowByNameWithMessageAndLastError
 402                 (env, JNU_JAVANETPKG "SocketException", "Error getting socket name");
 403             return;
 404         }
 405         localport = NET_GetPortFromSockaddr(&sa);
 406         (*env)->SetIntField(env, impl, psi_localportID, localport);
 407     } else {
 408         (*env)->SetIntField(env, impl, psi_localportID, localport);
 409     }
 410 }
 411 
 412 JNIEXPORT void JNICALL
 413 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketListen(JNIEnv *env, jobject this, jobject impl,
 414                                                jint count)
 415 {
 416     jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID);
 417     int fd;
 418 
 419     if (IS_NULL(fdObj)) {
 420         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 421                         "Socket closed");
 422         return;
 423     } else {
 424         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 425     }
 426 
 427     if (count == 0x7fffffff)
 428         count -= 1;
 429 
 430     int rv = rlisten(fd, count);
 431     if (rv == -1) {
 432         JNU_ThrowByNameWithMessageAndLastError
 433             (env, JNU_JAVANETPKG "SocketException", "Listen failed");
 434     }
 435 }
 436 
 437 JNIEXPORT void JNICALL
 438 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketAccept(JNIEnv *env, jobject this, jobject socket, jobject impl)
 439 {
 440     int port;
 441     jint timeout = (*env)->GetIntField(env, impl, psi_timeoutID);
 442     jlong prevNanoTime = 0;
 443     jlong nanoTimeout = (jlong) timeout * NET_NSEC_PER_MSEC;
 444     jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID);
 445 
 446     jobject socketFdObj;
 447     jobject socketAddressObj;
 448 
 449     jint fd;
 450 
 451     jint newfd;
 452 
 453     SOCKETADDRESS sa;
 454     socklen_t slen = sizeof(SOCKETADDRESS);
 455 
 456     if (IS_NULL(fdObj)) {
 457         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 458                         "Socket closed");
 459         return;
 460     } else {
 461         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 462     }
 463     if (IS_NULL(socket)) {
 464         JNU_ThrowNullPointerException(env, "socket is null");
 465         return;
 466     }
 467 
 468     for (;;) {
 469         int ret;
 470         jlong currNanoTime;
 471 
 472         if (prevNanoTime == 0 && nanoTimeout > 0) {
 473             prevNanoTime = JVM_NanoTime(env, 0);
 474         }
 475 
 476         if (timeout <= 0) {
 477             ret = RDMA_Timeout(env, fd, -1, 0);
 478         } else {
 479             ret = RDMA_Timeout(env, fd, nanoTimeout / NET_NSEC_PER_MSEC, prevNanoTime);
 480         }
 481 
 482         if (ret == 0) {
 483             JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
 484                             "Accept timed out");
 485             return;
 486         } else if (ret == -1) {
 487             if (errno == EBADF) {
 488                JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 489             } else if (errno == ENOMEM) {
 490                JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed");
 491             } else {
 492                JNU_ThrowByNameWithMessageAndLastError
 493                    (env, JNU_JAVANETPKG "SocketException", "Accept failed");
 494             }
 495             return;
 496         }
 497 
 498         newfd = RDMA_Accept(fd, &sa.sa, &slen);
 499 
 500         if (newfd >= 0) {
 501             SET_BLOCKING(newfd);
 502             break;
 503         }
 504 
 505         if (!(errno == ECONNABORTED || errno == EWOULDBLOCK)) {
 506             break;
 507         }
 508 
 509         if (nanoTimeout >= NET_NSEC_PER_MSEC) {
 510             currNanoTime = JVM_NanoTime(env, 0);
 511             nanoTimeout -= (currNanoTime - prevNanoTime);
 512             if (nanoTimeout < NET_NSEC_PER_MSEC) {
 513                 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
 514                         "Accept timed out");
 515                 return;
 516             }
 517             prevNanoTime = currNanoTime;
 518         }
 519     }
 520 
 521     if (newfd < 0) {
 522         if (newfd == -2) {
 523             JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
 524                             "operation interrupted");
 525         } else {
 526             if (errno == EINVAL) {
 527                 errno = EBADF;
 528             }
 529             if (errno == EBADF) {
 530                 JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed");
 531             } else {
 532                 JNU_ThrowByNameWithMessageAndLastError
 533                     (env, JNU_JAVANETPKG "SocketException", "Accept failed");
 534             }
 535         }
 536         return;
 537     }
 538 
 539     socketAddressObj = NET_SockaddrToInetAddress(env, &sa, &port);
 540     if (socketAddressObj == NULL) {
 541         rclose(newfd);
 542         return;
 543     }
 544 
 545     socketFdObj = (*env)->GetObjectField(env, socket, psi_fdID);
 546     (*env)->SetIntField(env, socketFdObj, IO_fd_fdID, newfd);
 547     (*env)->SetObjectField(env, socket, psi_addressID, socketAddressObj);
 548     (*env)->SetIntField(env, socket, psi_portID, port);
 549      port = (*env)->GetIntField(env, impl, psi_localportID);
 550     (*env)->SetIntField(env, socket, psi_localportID, port);
 551 }
 552 
 553 
 554 JNIEXPORT jint JNICALL
 555 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketAvailable(JNIEnv *env, jobject this, jobject impl) {
 556     jint ret = -1;
 557     jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID);
 558     jint fd;
 559 
 560     if (IS_NULL(fdObj)) {
 561         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 562                         "Socket closed");
 563         return -1;
 564     } else {
 565         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 566     }
 567     if (RDMA_SocketAvailable(fd, &ret) == 0){
 568         if (errno == ECONNRESET) {
 569             JNU_ThrowByName(env, "sun/net/ConnectionResetException", "");
 570         } else {
 571             JNU_ThrowByNameWithMessageAndLastError
 572                 (env, JNU_JAVANETPKG "SocketException", "ioctl FIONREAD failed");
 573         }
 574     }
 575     return ret;
 576 }
 577 
 578 JNIEXPORT void JNICALL
 579 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketClose(JNIEnv *env, jobject this,
 580                                               jboolean useDeferredClose, jobject impl) {
 581     jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID);
 582     jint fd;
 583 
 584     if (IS_NULL(fdObj)) {
 585         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 586                         "socket already closed");
 587         return;
 588     } else {
 589         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 590     }
 591     if (fd != -1) {
 592         if (useDeferredClose && marker_fd >= 0) {
 593             RDMA_Dup2(marker_fd, fd);
 594         } else {
 595             (*env)->SetIntField(env, fdObj, IO_fd_fdID, -1);
 596             RDMA_SocketClose(fd);
 597         }
 598     }
 599 }
 600 
 601 JNIEXPORT void JNICALL
 602 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketShutdown(JNIEnv *env, jobject this,
 603                                                 jint howto, jobject impl)
 604 {
 605     jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID);
 606     jint fd;
 607 
 608     if (IS_NULL(fdObj)) {
 609         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 610                         "socket already closed");
 611         return;
 612     } else {
 613         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 614     }
 615     rshutdown(fd, howto);
 616 }
 617 
 618 
 619 JNIEXPORT void JNICALL
 620 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketSetOption
 621   (JNIEnv *env, jobject this, jobject impl, jint cmd, jboolean on, jobject value)
 622 {
 623     int fd;
 624     int level, optname, optlen;
 625     union {
 626         int i;
 627         struct linger ling;
 628     } optval;
 629 
 630     fd = getFD(env, impl);
 631     if (fd < 0) {
 632         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 633                         "Socket closed");
 634         return;
 635     }
 636 
 637     if (cmd == java_net_SocketOptions_SO_TIMEOUT) {
 638         return;
 639     }
 640 
 641     if (RDMA_MapSocketOption(cmd, &level, &optname)) {
 642         JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");
 643         return;
 644     }
 645 
 646     switch (cmd) {
 647         case java_net_SocketOptions_SO_SNDBUF :
 648         case java_net_SocketOptions_SO_RCVBUF :
 649         case java_net_SocketOptions_SO_LINGER :
 650             {
 651                 jclass cls;
 652                 jfieldID fid;
 653 
 654                 cls = (*env)->FindClass(env, "java/lang/Integer");
 655                 CHECK_NULL(cls);
 656                 fid = (*env)->GetFieldID(env, cls, "value", "I");
 657                 CHECK_NULL(fid);
 658 
 659                 optval.i = (*env)->GetIntField(env, value, fid);
 660                 optlen = sizeof(optval.i);
 661 
 662                 break;
 663             }
 664 
 665         default :
 666             optval.i = (on ? 1 : 0);
 667             optlen = sizeof(optval.i);
 668 
 669     }
 670     if (RDMA_SetSockOpt(fd, level, optname, (const void *)&optval, optlen) < 0) {
 671         JNU_ThrowByNameWithMessageAndLastError
 672             (env, JNU_JAVANETPKG "SocketException", "Error setting socket option");
 673     }
 674 }
 675 
 676 JNIEXPORT jint JNICALL
 677 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketGetOption
 678   (JNIEnv *env, jobject this, jobject impl, jint cmd, jobject iaContainerObj)
 679 {
 680     int fd;
 681     int level, optname, optlen;
 682     union {
 683         int i;
 684         struct linger ling;
 685     } optval;
 686 
 687     fd = getFD(env, impl);
 688     if (fd < 0) {
 689         JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
 690                         "Socket closed");
 691         return -1;
 692     }
 693 
 694     if (cmd == java_net_SocketOptions_SO_BINDADDR) {
 695         SOCKETADDRESS sa;
 696         socklen_t len = sizeof(SOCKETADDRESS);
 697         int port;
 698         jobject iaObj;
 699         jclass iaCntrClass;
 700         jfieldID iaFieldID;
 701 
 702         if (rgetsockname(fd, &sa.sa, &len) < 0) {
 703             JNU_ThrowByNameWithMessageAndLastError
 704                 (env, JNU_JAVANETPKG "SocketException", "Error getting socket name");
 705             return -1;
 706         }
 707         iaObj = NET_SockaddrToInetAddress(env, &sa, &port);
 708         CHECK_NULL_RETURN(iaObj, -1);
 709 
 710         iaCntrClass = (*env)->GetObjectClass(env, iaContainerObj);
 711         iaFieldID = (*env)->GetFieldID(env, iaCntrClass, "addr", "Ljava/net/InetAddress;");
 712         CHECK_NULL_RETURN(iaFieldID, -1);
 713         (*env)->SetObjectField(env, iaContainerObj, iaFieldID, iaObj);
 714         return 0; 
 715     }
 716 
 717     if (RDMA_MapSocketOption(cmd, &level, &optname)) {
 718         JNU_ThrowByName(env, "java/net/SocketException", "Invalid option");
 719         return -1;
 720     }
 721 
 722     optlen = sizeof(optval.i);
 723 
 724     if (RDMA_GetSockOpt(fd, level, optname, (void *)&optval, &optlen) < 0) {
 725         JNU_ThrowByNameWithMessageAndLastError
 726             (env, JNU_JAVANETPKG "SocketException", "Error getting socket option");
 727         return -1;
 728     }
 729 
 730     switch (cmd) {
 731         case java_net_SocketOptions_SO_SNDBUF:
 732         case java_net_SocketOptions_SO_RCVBUF:
 733             return optval.i;
 734 
 735         default :
 736             return (optval.i == 0) ? -1 : 1;
 737     }
 738 }
 739 
 740 JNIEXPORT void JNICALL
 741 Java_rdma_ch_LinuxRdmaSocketImpl_rdmaSocketSendUrgentData(JNIEnv *env, jobject this, jobject impl,
 742                                              jint data) {
 743     jobject fdObj = (*env)->GetObjectField(env, impl, psi_fdID);
 744     int n, fd;
 745     unsigned char d = data & 0xFF;
 746 
 747     if (IS_NULL(fdObj)) {
 748         JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");
 749         return;
 750     } else {
 751         fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 752         if (fd == -1) {
 753             JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");
 754             return;
 755         }
 756 
 757     }
 758     n = RDMA_Send(fd, (char *)&d, 1, MSG_OOB);
 759     if (n == -1) {
 760         JNU_ThrowIOExceptionWithLastError(env, "Write failed");
 761     }
 762 }