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 package jdk.internal.net.rdma;
  27 
  28 import java.io.FileDescriptor;
  29 import java.io.IOException;
  30 import java.net.Inet4Address;
  31 import java.net.Inet6Address;
  32 import java.net.InetAddress;
  33 import java.net.InetSocketAddress;
  34 import java.net.ProtocolFamily;
  35 import java.net.SocketAddress;
  36 import java.net.SocketOption;
  37 import java.net.StandardProtocolFamily;
  38 import java.net.StandardSocketOptions;
  39 import java.nio.channels.UnsupportedAddressTypeException;
  40 import java.security.AccessController;
  41 import java.security.PrivilegedAction;
  42 import java.util.Enumeration;
  43 import sun.net.ext.RdmaSocketOptions;
  44 import sun.nio.ch.IOUtil;
  45 import sun.nio.ch.Net;
  46 
  47 public class RdmaNet {
  48     private RdmaNet() { }
  49 
  50     static final ProtocolFamily UNSPEC = new ProtocolFamily() {
  51         public String name() {
  52             return "UNSPEC";
  53         }
  54     };
  55 
  56     static boolean isReusePortAvailable() {
  57         return false;
  58     }
  59 
  60     private static volatile boolean checkedRdma;
  61     private static volatile boolean isRdmaAvailable;
  62 
  63     static boolean isRdmaAvailable() {
  64         if (!checkedRdma) {
  65             isRdmaAvailable = isRdmaAvailable0();
  66             checkedRdma = true;
  67         }
  68         return isRdmaAvailable;
  69     }
  70 
  71     private static native boolean isRdmaAvailable0();
  72 
  73     static InetSocketAddress checkAddress(SocketAddress sa, ProtocolFamily family) {
  74         InetSocketAddress isa = Net.checkAddress(sa);
  75         if (family == StandardProtocolFamily.INET) {
  76             InetAddress addr = isa.getAddress();
  77             if (!(addr instanceof Inet4Address))
  78                 throw new UnsupportedAddressTypeException();
  79         }
  80         if (family == StandardProtocolFamily.INET6) {
  81             InetAddress addr = isa.getAddress();
  82             if (!(addr instanceof Inet6Address))
  83                 throw new UnsupportedAddressTypeException();
  84         }
  85         return isa;
  86     }
  87 
  88     // -- Socket options
  89 
  90     static final RdmaSocketOptions rdmaOptions =
  91             RdmaSocketOptions.getInstance();
  92 
  93     static void setSocketOption(FileDescriptor fd, ProtocolFamily family,
  94             SocketOption<?> name, Object value) throws IOException
  95     {
  96         if (value == null)
  97             throw new IllegalArgumentException("Invalid option value");
  98 
  99         Class<?> type = name.type();
 100 
 101         if (rdmaOptions.isOptionSupported(name)) {
 102             rdmaOptions.setOption(fd, name, value);
 103             return;
 104         }
 105 
 106         if (type != Integer.class && type != Boolean.class)
 107             throw new AssertionError("Should not reach here");
 108 
 109         if (name == StandardSocketOptions.SO_RCVBUF ||
 110             name == StandardSocketOptions.SO_SNDBUF)
 111         {
 112             int i = ((Integer)value).intValue();
 113             if (i < 0)
 114                 throw new IllegalArgumentException(
 115                     "Invalid send/receive buffer size");
 116         }
 117         RdmaOptionKey key = RdmaSocketOptionRegistry.findOption(name, family);
 118         if (key == null)
 119             throw new AssertionError("Option not found");
 120 
 121         int arg;
 122         int maxValue = 1024 * 1024 * 1024 - 1; 
 123         if (type == Integer.class) {
 124             arg = ((Integer)value).intValue();
 125             if (arg > maxValue)
 126                 arg = maxValue;
 127         } else {
 128             boolean b = ((Boolean)value).booleanValue();
 129             arg = (b) ? 1 : 0;
 130         }
 131 
 132         boolean mayNeedConversion = (family == UNSPEC);
 133         setIntOption0(fd, mayNeedConversion, key.level(),
 134                       key.name(), arg);
 135     }
 136 
 137     static Object getSocketOption(FileDescriptor fd, ProtocolFamily family,
 138             SocketOption<?> name) throws IOException
 139     {
 140         Class<?> type = name.type();
 141 
 142         if (rdmaOptions.isOptionSupported(name)) {
 143             return rdmaOptions.getOption(fd, name);
 144         }
 145 
 146         if (type != Integer.class && type != Boolean.class)
 147             throw new AssertionError("Should not reach here");
 148 
 149         RdmaOptionKey key = RdmaSocketOptionRegistry.findOption(name, family);
 150         if (key == null)
 151             throw new AssertionError("Option not found");
 152 
 153         boolean mayNeedConversion = (family == UNSPEC);
 154         int value = getIntOption0(fd, mayNeedConversion, key.level(),
 155                                   key.name());
 156 
 157         if (type == Integer.class) {
 158             return Integer.valueOf(value);
 159         } else {
 160             return (value == 0) ? Boolean.FALSE : Boolean.TRUE;
 161         }
 162     }
 163 
 164     // -- Socket operations --
 165     static FileDescriptor socket(ProtocolFamily family, boolean stream)
 166             throws IOException {
 167         boolean preferIPv6 = Net.isIPv6Available() &&
 168                 (family != StandardProtocolFamily.INET);
 169         return IOUtil.newFD(socket0(preferIPv6, stream, false));
 170     }
 171 
 172     static FileDescriptor serverSocket(ProtocolFamily family, boolean stream)
 173             throws IOException {
 174         boolean preferIPv6 = Net.isIPv6Available() &&
 175                 (family != StandardProtocolFamily.INET);
 176         return IOUtil.newFD(socket0(preferIPv6, stream, true));
 177     }
 178 
 179     private static native int socket0(boolean preferIPv6, boolean stream,
 180             boolean reuse);
 181     static void bind(ProtocolFamily family, FileDescriptor fd,
 182             InetAddress addr, int port) throws IOException
 183     {
 184         boolean preferIPv6 = Net.isIPv6Available() &&
 185                 (family != StandardProtocolFamily.INET);
 186         bind0(fd, preferIPv6, addr, port);
 187     }
 188 
 189     private static native void bind0(FileDescriptor fd, boolean preferIPv6,
 190             InetAddress addr, int port) throws IOException;
 191 
 192     static native void listen(FileDescriptor fd, int backlog)
 193             throws IOException;
 194 
 195     static int connect(FileDescriptor fd, InetAddress remote, int remotePort)
 196             throws IOException
 197     {
 198         return connect(UNSPEC, fd, remote, remotePort);
 199     }
 200 
 201     static int connect(ProtocolFamily family, FileDescriptor fd,
 202             InetAddress remote, int remotePort) throws IOException
 203     {
 204         boolean preferIPv6 = Net.isIPv6Available() &&
 205                 (family != StandardProtocolFamily.INET);
 206         return connect0(preferIPv6, fd, remote, remotePort);
 207     }
 208 
 209     public static InetSocketAddress localAddress(FileDescriptor fd)
 210             throws IOException
 211     {
 212         return new InetSocketAddress(localInetAddress(fd), localPort(fd));
 213     }
 214 
 215     private static native int connect0(boolean preferIPv6, FileDescriptor fd,
 216             InetAddress remote, int remotePort) throws IOException;
 217 
 218     static native void shutdown(FileDescriptor fd, int how) throws IOException;
 219 
 220     private static native int localPort(FileDescriptor fd)
 221             throws IOException;
 222 
 223     private static native InetAddress localInetAddress(FileDescriptor fd)
 224             throws IOException;
 225 
 226     private static native int getIntOption0(FileDescriptor fd,
 227             boolean mayNeedConversion, int level, int opt) throws IOException;
 228 
 229     private static native void setIntOption0(FileDescriptor fd,
 230             boolean mayNeedConversion, int level, int opt, int arg)
 231             throws IOException;
 232 
 233     static native int poll(FileDescriptor fd, int events, long timeout)
 234             throws IOException;
 235 
 236     public static native void configureBlocking(FileDescriptor fd,
 237             boolean blocking); 
 238 
 239     private static native void initIDs();
 240 
 241     static {
 242         java.security.AccessController.doPrivileged(
 243             new java.security.PrivilegedAction<>() {
 244                 public Void run() {
 245                     System.loadLibrary("extnet");
 246                     return null;
 247                 }
 248             });
 249         IOUtil.load();
 250         initIDs();
 251     }
 252 }