1 /* 2 * Copyright (c) 2016, 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.net; 27 28 import java.net.*; 29 import java.nio.channels.*; 30 import java.io.IOException; 31 import java.lang.reflect.Field; 32 import java.util.Collections; 33 import java.util.HashMap; 34 import java.util.HashSet; 35 import java.util.Map; 36 import java.util.Set; 37 import rdma.ch.RdmaPollSelectorProvider; 38 import rdma.ch.RdmaSocketImpl; 39 import jdk.net.ExtendedSocketOptions.PlatformSocketOptions; 40 41 /** 42 * Defines static methods to set and get socket options defined by the 43 * {@link java.net.SocketOption} interface. All of the standard options defined 44 * by {@link java.net.Socket}, {@link java.net.ServerSocket}, and 45 * {@link java.net.DatagramSocket} can be set this way, as well as additional 46 * or platform specific options supported by each socket type. 47 * <p> 48 * The {@link #supportedOptions(Class)} method can be called to determine 49 * the complete set of options available (per socket type) on the 50 * current system. 51 * <p> 52 * The {@link #openRdmaSelector() openRdmaSelector}, {@link 53 * #openRdmaSocketChannel() openRdmaSocketChannel}, and {@link 54 * #openRdmaServerSocketChannel() openRdmaServerSocketChannel} methods 55 * create selectors and selectable channels for use with RDMA sockets. 56 * These objects are created a {@link java.nio.channels.spi.SelectorProvider 57 * SelectorProvider} that is not the default {@code SelectorProvider}. 58 * Consequently, selectable channels to RDMA sockets may not be multiplexed 59 * with selectable channel created by the default selector provider. The 60 * selector provider does not support datagram channels, its {@code 61 * openDatagramChannel} methods throw {@link UnsupportedOperationException}. 62 * When a security manager is installed, some non-standard socket options 63 * may require a security permission before being set or get. 64 * The details are specified in {@link ExtendedSocketOptions}. No permission 65 * is required for {@link java.net.StandardSocketOptions}. 66 * <p> 67 * 68 * @see java.nio.channels.NetworkChannel 69 */ 70 public class Sockets { 71 72 private static final Map<Class<?>,Set<SocketOption<?>>> 73 options = optionSets(); 74 75 private Sockets() {} 76 77 /** 78 * Sets the value of a socket option on a {@link java.net.Socket} 79 * 80 * @param s the socket 81 * @param name The socket option 82 * @param value The value of the socket option. May be null for some 83 * options. 84 * 85 * @throws UnsupportedOperationException if the socket does not support 86 * the option. 87 * 88 * @throws IllegalArgumentException if the value is not valid for 89 * the option. 90 * 91 * @throws IOException if an I/O error occurs, or socket is closed. 92 * 93 * @throws SecurityException if a security manager is set and the 94 * caller does not have any required permission. 95 * 96 * @throws NullPointerException if name is null 97 * 98 * @see java.net.StandardSocketOptions 99 */ 100 public static <T> void setOption(Socket s, SocketOption<T> name, T value) throws IOException 101 { 102 s.setOption(name, value); 103 } 104 105 /** 106 * Returns the value of a socket option from a {@link java.net.Socket} 107 * 108 * @param s the socket 109 * @param name The socket option 110 * 111 * @return The value of the socket option. 112 * 113 * @throws UnsupportedOperationException if the socket does not support 114 * the option. 115 * 116 * @throws IOException if an I/O error occurs 117 * 118 * @throws SecurityException if a security manager is set and the 119 * caller does not have any required permission. 120 * 121 * @throws NullPointerException if name is null 122 * 123 * @see java.net.StandardSocketOptions 124 */ 125 public static <T> T getOption(Socket s, SocketOption<T> name) throws IOException 126 { 127 return s.getOption(name); 128 } 129 130 /** 131 * Sets the value of a socket option on a {@link java.net.ServerSocket} 132 * 133 * @param s the socket 134 * @param name The socket option 135 * @param value The value of the socket option. 136 * 137 * @throws UnsupportedOperationException if the socket does not support 138 * the option. 139 * 140 * @throws IllegalArgumentException if the value is not valid for 141 * the option. 142 * 143 * @throws IOException if an I/O error occurs 144 * 145 * @throws NullPointerException if name is null 146 * 147 * @throws SecurityException if a security manager is set and the 148 * caller does not have any required permission. 149 * 150 * @see java.net.StandardSocketOptions 151 */ 152 public static <T> void setOption(ServerSocket s, SocketOption<T> name, T value) throws IOException 153 { 154 s.setOption(name, value); 155 } 156 157 /** 158 * Returns the value of a socket option from a {@link java.net.ServerSocket} 159 * 160 * @param s the socket 161 * @param name The socket option 162 * 163 * @return The value of the socket option. 164 * 165 * @throws UnsupportedOperationException if the socket does not support 166 * the option. 167 * 168 * @throws IOException if an I/O error occurs 169 * 170 * @throws NullPointerException if name is null 171 * 172 * @throws SecurityException if a security manager is set and the 173 * caller does not have any required permission. 174 * 175 * @see java.net.StandardSocketOptions 176 */ 177 public static <T> T getOption(ServerSocket s, SocketOption<T> name) throws IOException 178 { 179 return s.getOption(name); 180 } 181 182 /** 183 * Sets the value of a socket option on a {@link java.net.DatagramSocket} 184 * or {@link java.net.MulticastSocket} 185 * 186 * @param s the socket 187 * @param name The socket option 188 * @param value The value of the socket option. 189 * 190 * @throws UnsupportedOperationException if the socket does not support 191 * the option. 192 * 193 * @throws IllegalArgumentException if the value is not valid for 194 * the option. 195 * 196 * @throws IOException if an I/O error occurs 197 * 198 * @throws NullPointerException if name is null 199 * 200 * @throws SecurityException if a security manager is set and the 201 * caller does not have any required permission. 202 * 203 * @see java.net.StandardSocketOptions 204 */ 205 public static <T> void setOption(DatagramSocket s, SocketOption<T> name, T value) throws IOException 206 { 207 s.setOption(name, value); 208 } 209 210 /** 211 * Returns the value of a socket option from a 212 * {@link java.net.DatagramSocket} or {@link java.net.MulticastSocket} 213 * 214 * @param s the socket 215 * @param name The socket option 216 * 217 * @return The value of the socket option. 218 * 219 * @throws UnsupportedOperationException if the socket does not support 220 * the option. 221 * 222 * @throws IOException if an I/O error occurs 223 * 224 * @throws NullPointerException if name is null 225 * 226 * @throws SecurityException if a security manager is set and the 227 * caller does not have any required permission. 228 * 229 * @see java.net.StandardSocketOptions 230 */ 231 public static <T> T getOption(DatagramSocket s, SocketOption<T> name) throws IOException 232 { 233 return s.getOption(name); 234 } 235 236 /** 237 * Returns a set of {@link java.net.SocketOption}s supported by the 238 * given socket type. This set may include standard options and also 239 * non standard extended options. 240 * 241 * @param socketType the type of java.net socket 242 * 243 * @throws IllegalArgumentException if socketType is not a valid 244 * socket type from the java.net package. 245 */ 246 public static Set<SocketOption<?>> supportedOptions(Class<?> socketType) { 247 Set<SocketOption<?>> set = options.get(socketType); 248 if (set == null) { 249 throw new IllegalArgumentException("unknown socket type"); 250 } 251 return set; 252 } 253 254 private static void checkValueType(Object value, Class<?> type) { 255 if (!type.isAssignableFrom(value.getClass())) { 256 String s = "Found: " + value.getClass().toString() + " Expected: " 257 + type.toString(); 258 throw new IllegalArgumentException(s); 259 } 260 } 261 262 private static volatile boolean checkedReusePort; 263 private static volatile boolean isReusePortAvailable; 264 265 /** 266 * Tells whether SO_REUSEPORT is supported. 267 */ 268 static boolean isReusePortAvailable() { 269 if (!checkedReusePort) { 270 Set<SocketOption<?>> s = new Socket().supportedOptions(); 271 isReusePortAvailable = s.contains(StandardSocketOptions.SO_REUSEPORT); 272 checkedReusePort = true; 273 } 274 return isReusePortAvailable; 275 } 276 277 private static Map<Class<?>,Set<SocketOption<?>>> optionSets() { 278 Map<Class<?>,Set<SocketOption<?>>> options = new HashMap<>(); 279 boolean flowsupported = PlatformSocketOptions.get().flowSupported(); 280 boolean reuseportsupported = isReusePortAvailable(); 281 // Socket 282 283 Set<SocketOption<?>> set = new HashSet<>(); 284 set.add(StandardSocketOptions.SO_KEEPALIVE); 285 set.add(StandardSocketOptions.SO_SNDBUF); 286 set.add(StandardSocketOptions.SO_RCVBUF); 287 set.add(StandardSocketOptions.SO_REUSEADDR); 288 if (reuseportsupported) { 289 set.add(StandardSocketOptions.SO_REUSEPORT); 290 } 291 set.add(StandardSocketOptions.SO_LINGER); 292 set.add(StandardSocketOptions.IP_TOS); 293 set.add(StandardSocketOptions.TCP_NODELAY); 294 if (flowsupported) { 295 set.add(ExtendedSocketOptions.SO_FLOW_SLA); 296 } 297 if (QuickAck.available) { 298 set.add(ExtendedSocketOptions.TCP_QUICKACK); 299 } 300 if (KeepAliveOptions.AVAILABLE) { 301 set.addAll(Set.of(ExtendedSocketOptions.TCP_KEEPCOUNT, 302 ExtendedSocketOptions.TCP_KEEPIDLE, 303 ExtendedSocketOptions.TCP_KEEPINTERVAL)); 304 } 305 set = Collections.unmodifiableSet(set); 306 options.put(Socket.class, set); 307 308 // ServerSocket 309 310 set = new HashSet<>(); 311 set.add(StandardSocketOptions.SO_RCVBUF); 312 set.add(StandardSocketOptions.SO_REUSEADDR); 313 if (reuseportsupported) { 314 set.add(StandardSocketOptions.SO_REUSEPORT); 315 } 316 if (QuickAck.available) { 317 set.add(ExtendedSocketOptions.TCP_QUICKACK); 318 } 319 if (KeepAliveOptions.AVAILABLE) { 320 set.addAll(Set.of(ExtendedSocketOptions.TCP_KEEPCOUNT, 321 ExtendedSocketOptions.TCP_KEEPIDLE, 322 ExtendedSocketOptions.TCP_KEEPINTERVAL)); 323 } 324 set.add(StandardSocketOptions.IP_TOS); 325 set = Collections.unmodifiableSet(set); 326 options.put(ServerSocket.class, set); 327 328 // DatagramSocket 329 330 set = new HashSet<>(); 331 set.add(StandardSocketOptions.SO_SNDBUF); 332 set.add(StandardSocketOptions.SO_RCVBUF); 333 set.add(StandardSocketOptions.SO_REUSEADDR); 334 if (reuseportsupported) { 335 set.add(StandardSocketOptions.SO_REUSEPORT); 336 } 337 set.add(StandardSocketOptions.IP_TOS); 338 if (flowsupported) { 339 set.add(ExtendedSocketOptions.SO_FLOW_SLA); 340 } 341 set = Collections.unmodifiableSet(set); 342 options.put(DatagramSocket.class, set); 343 344 // MulticastSocket 345 346 set = new HashSet<>(); 347 set.add(StandardSocketOptions.SO_SNDBUF); 348 set.add(StandardSocketOptions.SO_RCVBUF); 349 set.add(StandardSocketOptions.SO_REUSEADDR); 350 if (reuseportsupported) { 351 set.add(StandardSocketOptions.SO_REUSEPORT); 352 } 353 set.add(StandardSocketOptions.IP_TOS); 354 set.add(StandardSocketOptions.IP_MULTICAST_IF); 355 set.add(StandardSocketOptions.IP_MULTICAST_TTL); 356 set.add(StandardSocketOptions.IP_MULTICAST_LOOP); 357 if (flowsupported) { 358 set.add(ExtendedSocketOptions.SO_FLOW_SLA); 359 } 360 set = Collections.unmodifiableSet(set); 361 options.put(MulticastSocket.class, set); 362 363 return Collections.unmodifiableMap(options); 364 } 365 366 /** 367 * Tells whether TCP_QUICKACK is supported. 368 */ 369 static class QuickAck { 370 371 static final boolean available; 372 373 static { 374 Set<SocketOption<?>> s = new Socket().supportedOptions(); 375 available = s.contains(ExtendedSocketOptions.TCP_QUICKACK); 376 } 377 } 378 379 /** 380 * Tells whether TCP_KEEPALIVE options are supported. 381 */ 382 static class KeepAliveOptions { 383 384 static final boolean AVAILABLE; 385 386 static { 387 Set<SocketOption<?>> s = new Socket().supportedOptions(); 388 AVAILABLE = s.containsAll(Set.of(ExtendedSocketOptions.TCP_KEEPCOUNT, 389 ExtendedSocketOptions.TCP_KEEPIDLE, 390 ExtendedSocketOptions.TCP_KEEPINTERVAL)); 391 } 392 } 393 394 395 /** 396 * Creates an unconnected RDMA socket. 397 * 398 * <p> A RDMA socket supports the same socket options that that {@code 399 * java.net.Socket} defines. In addition, it also supports the socket options 400 * specified by {@link RdmaSocketOptions}. 401 * 402 * @apiNote The rsocket implementation on Linux only supports IPv4 addresses. 403 * Consequently, attempting to bind or connect to an IPv6 address will fail 404 * with {@code IllegalArgumentException}. 405 * 406 * @throws IOException 407 * If an I/O error occurs 408 * @throws UnsupportedOperationException 409 * If RDMA sockets are not supported on this platform 410 * 411 * @since 12 412 */ 413 public static Socket openRdmaSocket() throws IOException { 414 RdmaSocketImpl impl = new RdmaSocketImpl(); 415 Socket s = new Socket(impl) {}; 416 return s; 417 } 418 419 /** 420 * Creates an unbound RDMA server socket. 421 * 422 * <p> A RDMA socket supports the same socket options that that {@code 423 * java.net.ServerSocket} defines. 424 * 425 * @apiNote The rsocket implementation on Linux only supports IPv4 addresses. 426 * Consequently, attempting to bind to an IPv6 address will fail with 427 * {@code IllegalArgumentException}. 428 * 429 * @throws IOException 430 * If an I/O error occurs 431 * @throws UnsupportedOperationException 432 * If RDMA sockets are not supported on this platform 433 * 434 * @since N 435 */ 436 public static ServerSocket openRdmaServerSocket() throws IOException { 437 SocketImpl impl = new RdmaSocketImpl(); 438 ServerSocket ss = new ServerSocket(impl) { 439 public Socket accept() throws IOException { 440 if (isClosed()) 441 throw new SocketException("Socket is closed"); 442 if (!isBound()) 443 throw new SocketException("Socket is not bound yet"); 444 445 Socket s = openRdmaSocket(); 446 implAccept(s); 447 return s; 448 } 449 }; 450 return ss; 451 } 452 453 /** 454 * Opens a socket channel to a RDMA socket. A newly-created socket channel 455 * is {@link SocketChannel#isOpen() open}, not yet bound to a {@link 456 * SocketChannel#getLocalAddress() local address}, and not yet 457 * {@link SocketChannel#isConnected() connected}. 458 * 459 * <p> A socket channel to a RDMA socket supports all of the socket options 460 * specified by {@code SocketChannel}. In addition, it also supports the 461 * socket options specified by {@link RdmaSocketOptions}. 462 * 463 * @apiNote The rsocket implementation on Linux only supports IPv4 addresses. 464 * Consequently, attempting to bind or connect to an IPv6 address will fail 465 * with {@code UnsupportedAddressTypeException}. 466 * 467 * @throws IOException 468 * If an I/O error occurs 469 * @throws UnsupportedOperationException 470 * If RDMA sockets are not supported on this platform 471 * 472 * @since 12 473 */ 474 public static SocketChannel openRdmaSocketChannel() throws IOException { 475 return RdmaPollSelectorProvider.provider().openSocketChannel(); 476 } 477 478 /** 479 * Opens a server-socket channel to a RDMA socket. A newly-created socket 480 * channel is {@link SocketChannel#isOpen() open} but not yet bound to a 481 * {@link SocketChannel#getLocalAddress() local address}. 482 * 483 * @apiNote The rsocket implementation on Linux only supports IPv4 addresses. 484 * Consequently, attempting to bind to an IPv6 address will fail with 485 * {@code UnsupportedAddressTypeException}. 486 * 487 * @throws IOException 488 * If an I/O error occurs 489 * @throws UnsupportedOperationException 490 * If RDMA sockets are not supported on this platform 491 * 492 * @since 12 493 */ 494 public static ServerSocketChannel openRdmaServerSocketChannel() 495 throws IOException { 496 return RdmaPollSelectorProvider.provider().openServerSocketChannel(); 497 } 498 499 /** 500 * Opens a selector to multiplex selectable channels to RDMA sockets. 501 * 502 * @throws IOException 503 * If an I/O error occurs 504 * @throws UnsupportedOperationException 505 * If RDMA sockets are not supported on this platform 506 * 507 * @since 12 508 */ 509 public static Selector openRdmaSelector() throws IOException { 510 return RdmaPollSelectorProvider.provider().openSelector(); 511 } 512 }