Repository: netty/netty
Issue: Netty Epoll fails in IPv6 only environment
Labels: defect
improvement

https://github.com/netty/netty/blob/83de77fbe5385130ba9afc666fecbe9b59bd8032/transport-native-unix-common/src/main/c/netty_unix_socket.c#L198

In this code, it tries to set dual stack sockets, but fails when running in an IPv6 only environment.  Such an environment exists in Google by blocking all IPv4 functionality in the kernel in order to prove code is IPv6 compatible.  

cc: @pmarks-net

Issue comments:
@Scottmitch please remember to set the milestone when you close issues so it is listed for the correct version.

Pull request review comments:
Please address the TODO 
Or just remove the fprintf for now
Please revert these changes as it seems this just moves code and so is not related
IIUC the static methods have to move up to avoid their first use case or we need to declare the method before the first use.
IIUC this method doesn't have to move (it isn't static and is declared in the header file)?
Why do we want to suppress the error here? `socket_type` accounts for `EAFNOSUPPORT` so if we get it here it seems like a programming error. Can we just propagate the error via `netty_unix_socket_setOptionHandleError` in this case too?
netty_unix_socket_setOption0 needs to be defined before _socket() uses it, and I wanted to keep most of these functions together.  

I can move the minimal number of functions to do so, or move _socket down.  Preference?
done.
Moving just the static methods sgtm
Why do we want to suppress the error here? socket_type accounts for EAFNOSUPPORT so if we get it here it seems like a programming error. Can we just propagate the error via netty_unix_socket_setOptionHandleError in this case too?
This was collapsed in https://github.com/netty/netty/pull/6994#discussion_r128362622
Sure, it doesn't have to, but some teams prefer to keep related code adjacent / contiguous if possible.  If you don't have such a requirement I can move the bare minimum.
That code checks if AF_INET6 is allowed, but not dual stack mode which is what `_socket()` is asking for.
Done.
Can we remote the `if` since it doesn't do anything? What about the following:

```c
if (errno != EAFNOSUPPORT) {
...
}
// else we failed to enable dual stack mode.
// It is assumed the socket is re‐stricted to sending and receiving IPv6 packets only.
```
Done.

Patch that resolved the issue:
diff --git a/transport-native-unix-common/src/main/c/netty_unix_socket.c b/transport-native-unix-common/src/main/c/netty_unix_socket.c
index 2cfb5e4d7d5..519f701c839 100644
--- a/transport-native-unix-common/src/main/c/netty_unix_socket.c
+++ b/transport-native-unix-common/src/main/c/netty_unix_socket.c
@@ -188,17 +188,40 @@ static int socket_type(JNIEnv* env) {
     }
 }
 
+static void netty_unix_socket_optionHandleError(JNIEnv* env, int err, char* method) {
+    if (err == EBADF) {
+        netty_unix_errors_throwClosedChannelException(env);
+    } else {
+        netty_unix_errors_throwChannelExceptionErrorNo(env, method, err);
+    }
+}
+
+static void netty_unix_socket_setOptionHandleError(JNIEnv* env, int err) {
+    netty_unix_socket_optionHandleError(env, err, "setsockopt() failed: ");
+}
+
+static int netty_unix_socket_setOption0(jint fd, int level, int optname, const void* optval, socklen_t len) {
+    return setsockopt(fd, level, optname, optval, len);
+}
+
 static jint _socket(JNIEnv* env, jclass clazz, int type) {
     int fd = nettyNonBlockingSocket(socketType, type, 0);
     if (fd == -1) {
         return -errno;
     } else if (socketType == AF_INET6) {
-        // Allow to listen /connect ipv4 and ipv6
+        // Try to allow listen /connect ipv4 and ipv6
         int optval = 0;
-        if (netty_unix_socket_setOption(env, fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval)) < 0) {
-            // Something went wrong so close the fd and return here. setOption(...) itself throws the exception already.
-            close(fd);
-            return -1;
+        if (netty_unix_socket_setOption0(fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval)) < 0) {
+            if (errno != EAFNOSUPPORT) {
+              netty_unix_socket_setOptionHandleError(env, errno);
+              // Something went wrong so close the fd and return here. setOption(...) itself throws the exception already.
+              close(fd);
+              return -1;
+            }
+            // else we failed to enable dual stack mode.
+            // It is assumed the socket is re‐stricted to sending and receiving IPv6 packets only.
+            // Don't close fd and don't return -1. At best we can do is log.
+            // TODO: bubble this up to an actual Logger.
         }
     }
     return fd;
@@ -289,26 +312,10 @@ static jobject _recvFrom(JNIEnv* env, jint fd, void* buffer, jint pos, jint limi
     return createDatagramSocketAddress(env, &addr, res);
 }
 
-static void netty_unix_socket_optionHandleError(JNIEnv* env, int err, char* method) {
-    if (err == EBADF) {
-        netty_unix_errors_throwClosedChannelException(env);
-    } else {
-        netty_unix_errors_throwChannelExceptionErrorNo(env, method, err);
-    }
-}
-
-static void netty_unix_socket_setOptionHandleError(JNIEnv* env, int err) {
-    netty_unix_socket_optionHandleError(env, err, "setsockopt() failed: ");
-}
-
 static void netty_unix_socket_getOptionHandleError(JNIEnv* env, int err) {
     netty_unix_socket_optionHandleError(env, err, "getsockopt() failed: ");
 }
 
-static int netty_unix_socket_setOption0(jint fd, int level, int optname, const void* optval, socklen_t len) {
-    return setsockopt(fd, level, optname, optval, len);
-}
-
 static int netty_unix_socket_getOption0(jint fd, int level, int optname, void* optval, socklen_t optlen) {
     return getsockopt(fd, level, optname, optval, &optlen);
 }


Test patch: