diff --git a/src/jdk.net/linux/classes/jdk/net/LinuxSocketOptions.java b/src/jdk.net/linux/classes/jdk/net/LinuxSocketOptions.java
index 50d0aeda0f9..e0ded9dd364 100644
--- a/src/jdk.net/linux/classes/jdk/net/LinuxSocketOptions.java
+++ b/src/jdk.net/linux/classes/jdk/net/LinuxSocketOptions.java
@@ -84,6 +84,16 @@ class LinuxSocketOptions extends PlatformSocketOptions {
         return getTcpKeepAliveIntvl0(fd);
     }
 
+    @Override
+    boolean incomingNapiIdSupported() {
+        return incomingNapiIdSupported0();
+    }
+
+    @Override
+    int getIncomingNapiId(int fd) throws SocketException {
+        return getIncomingNapiId0(fd);
+    }
+
     private static native void setTcpkeepAliveProbes0(int fd, int value) throws SocketException;
     private static native void setTcpKeepAliveTime0(int fd, int value) throws SocketException;
     private static native void setTcpKeepAliveIntvl0(int fd, int value) throws SocketException;
@@ -94,6 +104,8 @@ class LinuxSocketOptions extends PlatformSocketOptions {
     private static native boolean getQuickAck0(int fd) throws SocketException;
     private static native boolean keepAliveOptionsSupported0();
     private static native boolean quickAckSupported0();
+    private static native boolean incomingNapiIdSupported0();
+    private static native int getIncomingNapiId0(int fd) throws SocketException;
     static {
         if (System.getSecurityManager() == null) {
             System.loadLibrary("extnet");
diff --git a/src/jdk.net/linux/native/libextnet/LinuxSocketOptions.c b/src/jdk.net/linux/native/libextnet/LinuxSocketOptions.c
index 2b4126e1a95..369fe6b3130 100644
--- a/src/jdk.net/linux/native/libextnet/LinuxSocketOptions.c
+++ b/src/jdk.net/linux/native/libextnet/LinuxSocketOptions.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -33,6 +33,39 @@
 #include "jni_util.h"
 #include "jdk_net_LinuxSocketOptions.h"
 
+#ifndef SO_INCOMING_NAPI_ID
+#define SO_INCOMING_NAPI_ID    56
+#endif
+
+static void handleError(JNIEnv *env, jint rv, const char *errmsg) {
+    if (rv < 0) {
+        if (errno == ENOPROTOOPT) {
+            JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
+                    "unsupported socket option");
+        } else {
+            JNU_ThrowByNameWithLastError(env, "java/net/SocketException", errmsg);
+        }
+    }
+}
+
+static jint socketOptionSupported(jint level, jint optname) {
+    jint one = 1;
+    jint rv, s;
+    socklen_t sz = sizeof (one);
+    s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (s < 0) {
+        return 0;
+    }
+    rv = getsockopt(s, level, optname, (void *) &one, &sz);
+    if (rv != 0 && errno == ENOPROTOOPT) {
+        rv = 0;
+    } else {
+        rv = 1;
+    }
+    close(s);
+    return rv;
+}
+
 /*
  * Declare library specific JNI_Onload entry if static build
  */
@@ -49,15 +82,7 @@ JNIEXPORT void JNICALL Java_jdk_net_LinuxSocketOptions_setQuickAck0
     int rv;
     optval = (on ? 1 : 0);
     rv = setsockopt(fd, SOL_SOCKET, TCP_QUICKACK, &optval, sizeof (optval));
-    if (rv < 0) {
-        if (errno == ENOPROTOOPT) {
-            JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
-                            "unsupported socket option");
-        } else {
-            JNU_ThrowByNameWithLastError(env, "java/net/SocketException",
-                                        "set option TCP_QUICKACK failed");
-        }
-    }
+    handleError(env, rv, "set option TCP_QUICKACK failed");
 }
 
 /*
@@ -70,15 +95,7 @@ JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_getQuickAck0
     int on;
     socklen_t sz = sizeof (on);
     int rv = getsockopt(fd, SOL_SOCKET, TCP_QUICKACK, &on, &sz);
-    if (rv < 0) {
-        if (errno == ENOPROTOOPT) {
-            JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
-                            "unsupported socket option");
-        } else {
-            JNU_ThrowByNameWithLastError(env, "java/net/SocketException",
-                                        "get option TCP_QUICKACK failed");
-        }
-    }
+    handleError(env, rv, "get option TCP_QUICKACK failed");
     return on != 0;
 }
 
@@ -89,48 +106,7 @@ JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_getQuickAck0
  */
 JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_quickAckSupported0
 (JNIEnv *env, jobject unused) {
-    int one = 1;
-    int rv, s;
-    s = socket(PF_INET, SOCK_STREAM, 0);
-    if (s < 0) {
-        return JNI_FALSE;
-    }
-    rv = setsockopt(s, SOL_SOCKET, TCP_QUICKACK, (void *) &one, sizeof (one));
-    if (rv != 0 && errno == ENOPROTOOPT) {
-        rv = JNI_FALSE;
-    } else {
-        rv = JNI_TRUE;
-    }
-    close(s);
-    return rv;
-}
-
-static jint socketOptionSupported(jint sockopt) {
-    jint one = 1;
-    jint rv, s;
-    s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
-    if (s < 0) {
-        return 0;
-    }
-    rv = setsockopt(s, SOL_TCP, sockopt, (void *) &one, sizeof (one));
-    if (rv != 0 && errno == ENOPROTOOPT) {
-        rv = 0;
-    } else {
-        rv = 1;
-    }
-    close(s);
-    return rv;
-}
-
-static void handleError(JNIEnv *env, jint rv, const char *errmsg) {
-    if (rv < 0) {
-        if (errno == ENOPROTOOPT) {
-            JNU_ThrowByName(env, "java/lang/UnsupportedOperationException",
-                    "unsupported socket option");
-        } else {
-            JNU_ThrowByNameWithLastError(env, "java/net/SocketException", errmsg);
-        }
-    }
+    return socketOptionSupported(SOL_SOCKET, TCP_QUICKACK);
 }
 
 /*
@@ -140,8 +116,8 @@ static void handleError(JNIEnv *env, jint rv, const char *errmsg) {
  */
 JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_keepAliveOptionsSupported0
 (JNIEnv *env, jobject unused) {
-    return socketOptionSupported(TCP_KEEPIDLE) && socketOptionSupported(TCP_KEEPCNT)
-            && socketOptionSupported(TCP_KEEPINTVL);
+    return socketOptionSupported(SOL_TCP, TCP_KEEPIDLE) && socketOptionSupported(SOL_TCP, TCP_KEEPCNT)
+            && socketOptionSupported(SOL_TCP, TCP_KEEPINTVL);
 }
 
 /*
@@ -218,3 +194,27 @@ JNIEXPORT jint JNICALL Java_jdk_net_LinuxSocketOptions_getTcpKeepAliveIntvl0
     handleError(env, rv, "get option TCP_KEEPINTVL failed");
     return optval;
 }
+
+/*
+ * Class:     jdk_net_LinuxSocketOptions
+ * Method:    incomingNapiIdSupported0
+ * Signature: ()Z;
+ */
+JNIEXPORT jboolean JNICALL Java_jdk_net_LinuxSocketOptions_incomingNapiIdSupported0
+(JNIEnv *env, jobject unused) {
+    return socketOptionSupported(SOL_SOCKET, SO_INCOMING_NAPI_ID);
+}
+
+/*
+ * Class:     jdk_net_LinuxSocketOptions
+ * Method:    getIncomingNapiId0
+ * Signature: (I)I;
+ */
+JNIEXPORT jint JNICALL Java_jdk_net_LinuxSocketOptions_getIncomingNapiId0
+(JNIEnv *env, jobject unused, jint fd) {
+    jint optval, rv;
+    socklen_t sz = sizeof (optval);
+    rv = getsockopt(fd, SOL_SOCKET, SO_INCOMING_NAPI_ID, &optval, &sz);
+    handleError(env, rv, "get option SO_INCOMING_NAPI_ID failed");
+    return optval;
+}
diff --git a/src/jdk.net/share/classes/jdk/net/ExtendedSocketOptions.java b/src/jdk.net/share/classes/jdk/net/ExtendedSocketOptions.java
index 5aa05520753..53215507c89 100644
--- a/src/jdk.net/share/classes/jdk/net/ExtendedSocketOptions.java
+++ b/src/jdk.net/share/classes/jdk/net/ExtendedSocketOptions.java
@@ -165,6 +165,34 @@ public final class ExtendedSocketOptions {
     public static final SocketOption<Integer> TCP_KEEPCOUNT
             = new ExtSocketOption<Integer>("TCP_KEEPCOUNT", Integer.class);
 
+    /**
+     * Identifies the receive queue that the last incoming packet for the socket
+     * was received on.
+     *
+     * <p> The value of this socket option is a positive {@code Integer} that
+     * identifies a receive queue that the application can use to split the
+     * incoming flows among threads based on the queue identifier. The value is
+     * {@code 0} when the socket is not bound, a packet has not been received,
+     * or more generally, when there is no receive queue to identify.
+     * The socket option is supported by both stream-oriented and datagram-oriented
+     * sockets.
+     *
+     * <p> The socket option is read-only and an attempt to set the socket option
+     * will throw {@code SocketException}.
+     *
+     * @apiNote
+     * Network devices may have multiple queues or channels to transmit and receive
+     * network packets. The {@code SO_INCOMING_NAPI_ID} socket option provides a hint
+     * to the application to indicate the receive queue on which an incoming socket
+     * connection or packets for that connection are directed to. An application may
+     * take advantage of this by handling all socket connections assigned to a
+     * specific queue on one thread.
+     *
+     * @since 15
+     */
+    public static final SocketOption<Integer> SO_INCOMING_NAPI_ID
+            = new ExtSocketOption<Integer>("SO_INCOMING_NAPI_ID", Integer.class);
+
     private static final PlatformSocketOptions platformSocketOptions =
             PlatformSocketOptions.get();
 
@@ -174,6 +202,8 @@ public final class ExtendedSocketOptions {
             platformSocketOptions.quickAckSupported();
     private static final boolean keepAliveOptSupported =
             platformSocketOptions.keepAliveOptionsSupported();
+    private static final boolean incomingNapiIdOptSupported  =
+            platformSocketOptions.incomingNapiIdSupported();
     private static final Set<SocketOption<?>> extendedOptions = options();
 
     static Set<SocketOption<?>> options() {
@@ -184,6 +214,9 @@ public final class ExtendedSocketOptions {
         if (quickAckSupported) {
             options.add(TCP_QUICKACK);
         }
+        if (incomingNapiIdOptSupported) {
+            options.add(SO_INCOMING_NAPI_ID);
+        }
         if (keepAliveOptSupported) {
             options.addAll(Set.of(TCP_KEEPCOUNT, TCP_KEEPIDLE, TCP_KEEPINTERVAL));
         }
@@ -221,6 +254,11 @@ public final class ExtendedSocketOptions {
                     setTcpKeepAliveTime(fd, (Integer) value);
                 } else if (option == TCP_KEEPINTERVAL) {
                     setTcpKeepAliveIntvl(fd, (Integer) value);
+                } else if (option == SO_INCOMING_NAPI_ID) {
+                    if (!incomingNapiIdOptSupported)
+                        throw new UnsupportedOperationException("Attempt to set unsupported option " + option);
+                    else
+                        throw new SocketException("Attempt to set read only option " + option);
                 } else {
                     throw new InternalError("Unexpected option " + option);
                 }
@@ -252,6 +290,8 @@ public final class ExtendedSocketOptions {
                     return getTcpKeepAliveTime(fd);
                 } else if (option == TCP_KEEPINTERVAL) {
                     return getTcpKeepAliveIntvl(fd);
+                } else if (option == SO_INCOMING_NAPI_ID) {
+                    return getIncomingNapiId(fd);
                 } else {
                     throw new InternalError("Unexpected option " + option);
                 }
@@ -325,6 +365,10 @@ public final class ExtendedSocketOptions {
         return platformSocketOptions.getTcpKeepAliveIntvl(fdAccess.get(fd));
     }
 
+    private static int getIncomingNapiId(FileDescriptor fd) throws SocketException {
+        return platformSocketOptions.getIncomingNapiId(fdAccess.get(fd));
+    }
+
     static class PlatformSocketOptions {
 
         protected PlatformSocketOptions() {}
@@ -418,5 +462,13 @@ public final class ExtendedSocketOptions {
         int getTcpKeepAliveIntvl(int fd) throws SocketException {
             throw new UnsupportedOperationException("unsupported TCP_KEEPINTVL option");
         }
+
+        boolean incomingNapiIdSupported() {
+            return false;
+        }
+
+        int getIncomingNapiId(int fd) throws SocketException {
+            throw new UnsupportedOperationException("unsupported SO_INCOMING_NAPI_ID socket option");
+        }
     }
 }
diff --git a/src/jdk.net/share/classes/jdk/net/Sockets.java b/src/jdk.net/share/classes/jdk/net/Sockets.java
index 4dddf9570b2..60bb522c0a0 100644
--- a/src/jdk.net/share/classes/jdk/net/Sockets.java
+++ b/src/jdk.net/share/classes/jdk/net/Sockets.java
@@ -263,6 +263,8 @@ public class Sockets {
     private static Map<Class<?>,Set<SocketOption<?>>> optionSets() {
         Map<Class<?>,Set<SocketOption<?>>> options = new HashMap<>();
         boolean flowsupported = PlatformSocketOptions.get().flowSupported();
+        boolean incomingNapiIdsupported = PlatformSocketOptions.get().incomingNapiIdSupported();
+
         boolean reuseportsupported = isReusePortAvailable();
         // Socket
 
@@ -288,6 +290,9 @@ public class Sockets {
                     ExtendedSocketOptions.TCP_KEEPIDLE,
                     ExtendedSocketOptions.TCP_KEEPINTERVAL));
         }
+        if (incomingNapiIdsupported) {
+            set.add(ExtendedSocketOptions.SO_INCOMING_NAPI_ID);
+        }
         set = Collections.unmodifiableSet(set);
         options.put(Socket.class, set);
 
@@ -308,6 +313,9 @@ public class Sockets {
                     ExtendedSocketOptions.TCP_KEEPINTERVAL));
         }
         set.add(StandardSocketOptions.IP_TOS);
+        if (incomingNapiIdsupported) {
+            set.add(ExtendedSocketOptions.SO_INCOMING_NAPI_ID);
+        }
         set = Collections.unmodifiableSet(set);
         options.put(ServerSocket.class, set);
 
@@ -324,6 +332,9 @@ public class Sockets {
         if (flowsupported) {
             set.add(ExtendedSocketOptions.SO_FLOW_SLA);
         }
+        if (incomingNapiIdsupported) {
+            set.add(ExtendedSocketOptions.SO_INCOMING_NAPI_ID);
+        }
         set = Collections.unmodifiableSet(set);
         options.put(DatagramSocket.class, set);
 
diff --git a/test/jdk/java/net/SocketOption/AfterClose.java b/test/jdk/java/net/SocketOption/AfterClose.java
index 5d07beb3e76..3e502d2d560 100644
--- a/test/jdk/java/net/SocketOption/AfterClose.java
+++ b/test/jdk/java/net/SocketOption/AfterClose.java
@@ -59,6 +59,7 @@ import static org.testng.Assert.expectThrows;
 public class AfterClose {
 
     static final Class<IOException> IOE = IOException.class;
+    static final String RO = "READ_ONLY";
 
     static Map<SocketOption<?>,List<Object>> OPTION_VALUES_MAP = optionValueMap();
 
@@ -106,6 +107,8 @@ public class AfterClose {
             map.put((SocketOption<?>)field.get(null), listOf(10, 100));
             field = c.getField("TCP_KEEPCOUNT");
             map.put((SocketOption<?>)field.get(null), listOf(10, 100));
+            field = c.getField("SO_INCOMING_NAPI_ID");
+            map.put((SocketOption<?>)field.get(null), listOf(RO));
         } catch (ClassNotFoundException e) {
             // ignore, jdk.net module not present
         } catch (ReflectiveOperationException e) {
@@ -158,7 +161,7 @@ public class AfterClose {
         Socket socket = createClosedSocketFromAdapter();
         for (int i=0; i<3; i++); {
             for (T value : values) {
-                expectThrows(IOE, () -> socket.setOption(option, value));
+                if (!RO.equals(value)) expectThrows(IOE, () -> socket.setOption(option, value));
                 expectThrows(IOE, () -> socket.getOption(option));
             }
         }
@@ -211,7 +214,7 @@ public class AfterClose {
         ServerSocket serverSocket = createClosedServerSocketFromAdapter();
         for (int i=0; i<3; i++); {
             for (T value : values) {
-                expectThrows(IOE, () -> serverSocket.setOption(option, value));
+                if (!RO.equals(value)) expectThrows(IOE, () -> serverSocket.setOption(option, value));
                 expectThrows(IOE, () -> serverSocket.getOption(option));
             }
         }
diff --git a/test/jdk/java/nio/channels/etc/PrintSupportedOptions.java b/test/jdk/java/nio/channels/etc/PrintSupportedOptions.java
index 95c73ee2145..dd7970520e4 100644
--- a/test/jdk/java/nio/channels/etc/PrintSupportedOptions.java
+++ b/test/jdk/java/nio/channels/etc/PrintSupportedOptions.java
@@ -33,6 +33,7 @@
 import java.io.IOException;
 import java.net.SocketOption;
 import java.nio.channels.*;
+import java.util.*;
 
 import jdk.test.lib.net.IPSupport;
 
@@ -54,6 +55,8 @@ public class PrintSupportedOptions {
         test(() -> AsynchronousServerSocketChannel.open());
     }
 
+    static final Set<String> READ_ONLY_OPTS = Set.of("SO_INCOMING_NAPI_ID");
+
     @SuppressWarnings("unchecked")
     static <T extends NetworkChannel>
     void test(NetworkChannelSupplier<T> supplier) throws IOException {
@@ -62,8 +65,9 @@ public class PrintSupportedOptions {
             for (SocketOption<?> opt : ch.supportedOptions()) {
                 Object value = ch.getOption(opt);
                 System.out.format(" %s -> %s%n", opt.name(), value);
-                if (value != null) {
-                    ch.setOption((SocketOption<Object>) opt, value);
+                if (!READ_ONLY_OPTS.contains(opt.name())) {
+                    if (value != null)
+                        ch.setOption((SocketOption<Object>) opt, value);
                 }
             }
         }