Repository: netty/netty
Issue: Implement IP_FREEBIND and Any-IP in native JNI base epoll
Labels: feature

It would be really nice to have netty support IP_FREEBIND and Any-IP.

Essentially it allows you to bind() to any IP address, even if it's not associated with an Interface.

I was thinking this could be really helpful in ipv6 setups whereby you just setup your webserver on a subnet with 16k IPs and then when you want to deploy a new webapp, you just give it one of those IPs.

The problem is that your box wouldn't want to manually add every IP as your ifconfig would then list 16k IPs.

With Any-IP you can add a whole subnet in one command.

Then you can bind sockets to it..

The PROBLEM is I can't find out how to do this with Java .  Using netty would allow you to build really awesome apps this way but I don't think you can bind sockets this way in Java.

The C programmers always seem to get the cool toys before we do!

http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=ab79ad14a2d51e95f0ac3cef7cd116a57089ba82

  IP_FREEBIND (since Linux 2.4)
              If enabled, this boolean option allows binding to an IP
              address that is nonlocal or does not (yet) exist.  This
              permits listening on a socket, without requiring the
              underlying network interface or the specified dynamic IP
              address to be up at the time that the application is trying to
              bind to it.  This option is the per-socket equivalent of the
              ip_nonlocal_bind /proc interface described below.

... I may be looking at this in the next few weeks/months and if so will just implement it myself.


Issue comments:
@burtonator - Thanks for reaching out! Sounds like a useful feature. I'll put this on the todo list but if you get around to it before we do please feel free to submit a PR.

Thanks will check once i have free cycles

> Am 25.07.2015 um 00:20 schrieb Kevin Burton notifications@github.com:
> 
> It would be really nice to have netty support IP_FREEBIND and Any-IP.
> 
> Essentially it allows you to bind() to any IP address, even if it's not associated with an Interface.
> 
> I was thinking this could be really helpful in ipv6 setups whereby you just setup your webserver on a subnet with 16k IPs and then when you want to deploy a new webapp, you just give it one of those IPs.
> 
> The problem is that your box wouldn't want to manually add every IP as your ifconfig would then list 16k IPs.
> 
> With Any-IP you can add a whole subnet in one command.
> 
> Then you can bind sockets to it..
> 
> The PROBLEM is I can't find out how to do this with Java . Using netty would allow you to build really awesome apps this way but I don't think you can bind sockets this way in Java.
> 
> The C programmers always seem to get the cool toys before we do!
> 
> http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=ab79ad14a2d51e95f0ac3cef7cd116a57089ba82
> 
> IP_FREEBIND (since Linux 2.4)
> If enabled, this boolean option allows binding to an IP
> address that is nonlocal or does not (yet) exist. This
> permits listening on a socket, without requiring the
> underlying network interface or the specified dynamic IP
> address to be up at the time that the application is trying to
> bind to it. This option is the per-socket equivalent of the
> ip_nonlocal_bind /proc interface described below.
> 
> ... I may be looking at this in the next few weeks/months and if so will just implement it myself.
> 
> —
> Reply to this email directly or view it on GitHub.

@burtonator please check https://github.com/netty/netty/pull/4034

Fixed by https://github.com/netty/netty/pull/4034


Pull request review comments:

Patch that resolved the issue:
diff --git a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c
index a277d1695a5..480bf50a1c7 100644
--- a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c
+++ b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c
@@ -1248,6 +1248,10 @@ JNIEXPORT void Java_io_netty_channel_epoll_Native_setTcpKeepCnt(JNIEnv* env, jcl
     setOption(env, fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, sizeof(optval));
 }
 
+JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setIpFreeBind(JNIEnv* env, jclass clazz, jint fd, jint optval) {
+    setOption(env, fd, IPPROTO_IP, IP_FREEBIND, &optval, sizeof(optval));
+}
+
 JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isReuseAddresss(JNIEnv* env, jclass clazz, jint fd) {
     int optval;
     if (getOption(env, fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) {
@@ -1364,6 +1368,14 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepCnt(JNIEnv*
      return optval;
 }
 
+JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isIpFreeBind(JNIEnv* env, jclass clazz, jint fd) {
+     int optval;
+     if (getOption(env, fd, IPPROTO_TCP, IP_FREEBIND, &optval, sizeof(optval)) == -1) {
+         return -1;
+     }
+     return optval;
+}
+
 JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_tcpInfo0(JNIEnv* env, jclass clazz, jint fd, jintArray array) {
      struct tcp_info tcp_info;
      if (getOption(env, fd, IPPROTO_TCP, TCP_INFO, &tcp_info, sizeof(tcp_info)) == -1) {
diff --git a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h
index 4708557ab11..380bd3b20f6 100644
--- a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h
+++ b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h
@@ -87,6 +87,7 @@ void Java_io_netty_channel_epoll_Native_setBroadcast(JNIEnv* env, jclass clazz,
 void Java_io_netty_channel_epoll_Native_setTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd, jint optval);
 void Java_io_netty_channel_epoll_Native_setTcpKeepIntvl(JNIEnv* env, jclass clazz, jint fd, jint optval);
 void Java_io_netty_channel_epoll_Native_setTcpKeepCnt(JNIEnv* env, jclass clazz, jint fd, jint optval);
+void Java_io_netty_channel_epoll_Native_setIpFreeBind(JNIEnv* env, jclass clazz, jint fd, jint optval);
 
 jint Java_io_netty_channel_epoll_Native_isReuseAddresss(JNIEnv* env, jclass clazz, jint fd);
 jint Java_io_netty_channel_epoll_Native_isReusePort(JNIEnv* env, jclass clazz, jint fd);
@@ -102,6 +103,7 @@ jint Java_io_netty_channel_epoll_Native_getTcpKeepIdle(JNIEnv* env, jclass clazz
 jint Java_io_netty_channel_epoll_Native_getTcpKeepIntvl(JNIEnv* env, jclass clazz, jint fd);
 jint Java_io_netty_channel_epoll_Native_getTcpKeepCnt(JNIEnv* env, jclass clazz, jint fd);
 jint Java_io_netty_channel_epoll_Native_getSoError(JNIEnv* env, jclass clazz, jint fd);
+jint Java_io_netty_channel_epoll_Native_isIpFreeBind(JNIEnv* env, jclass clazz, jint fd);
 
 jstring Java_io_netty_channel_epoll_Native_kernelVersion(JNIEnv* env, jclass clazz);
 jint Java_io_netty_channel_epoll_Native_iovMax(JNIEnv* env, jclass clazz);
diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelOption.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelOption.java
index 268366f4012..4a3c5d19b55 100644
--- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelOption.java
+++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelOption.java
@@ -26,6 +26,7 @@ public final class EpollChannelOption<T> extends ChannelOption<T> {
     public static final ChannelOption<Integer> TCP_KEEPIDLE = valueOf("TCP_KEEPIDLE");
     public static final ChannelOption<Integer> TCP_KEEPINTVL = valueOf("TCP_KEEPINTVL");
     public static final ChannelOption<Integer> TCP_KEEPCNT = valueOf("TCP_KEEPCNT");
+    public static final ChannelOption<Boolean> IP_FREEBIND = valueOf("IP_FREEBIND");
 
     public static final ChannelOption<DomainSocketReadMode> DOMAIN_SOCKET_READ_MODE =
             valueOf("DOMAIN_SOCKET_READ_MODE");
diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerSocketChannelConfig.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerSocketChannelConfig.java
index c1ee1c0d521..e6888eb55e1 100644
--- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerSocketChannelConfig.java
+++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerSocketChannelConfig.java
@@ -37,7 +37,7 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
 
     @Override
     public Map<ChannelOption<?>, Object> getOptions() {
-        return getOptions(super.getOptions(), EpollChannelOption.SO_REUSEPORT);
+        return getOptions(super.getOptions(), EpollChannelOption.SO_REUSEPORT, EpollChannelOption.IP_FREEBIND);
     }
 
     @SuppressWarnings("unchecked")
@@ -46,6 +46,9 @@ public <T> T getOption(ChannelOption<T> option) {
         if (option == EpollChannelOption.SO_REUSEPORT) {
             return (T) Boolean.valueOf(isReusePort());
         }
+        if (option == EpollChannelOption.IP_FREEBIND) {
+            return (T) Boolean.valueOf(isFreeBind());
+        }
         return super.getOption(option);
     }
 
@@ -55,6 +58,8 @@ public <T> boolean setOption(ChannelOption<T> option, T value) {
 
         if (option == EpollChannelOption.SO_REUSEPORT) {
             setReusePort((Boolean) value);
+        } else if (option == EpollChannelOption.IP_FREEBIND) {
+            setFreeBind((Boolean) value);
         } else {
             return super.setOption(option, value);
         }
@@ -157,4 +162,21 @@ public EpollServerSocketChannelConfig setReusePort(boolean reusePort) {
         Native.setReusePort(channel.fd().intValue(), reusePort ? 1 : 0);
         return this;
     }
+
+    /**
+     * Returns {@code true} if <a href="http://man7.org/linux/man-pages/man7/ip.7.html">IP_FREEBIND</a> is enabled,
+     * {@code false} otherwise.
+     */
+    public boolean isFreeBind() {
+        return Native.isIpFreeBind(channel.fd().intValue()) != 0;
+    }
+
+    /**
+     * If {@code true} is used <a href="http://man7.org/linux/man-pages/man7/ip.7.html">IP_FREEBIND</a> is enabled,
+     * {@code false} for disable it. Default is disabled.
+     */
+    public EpollServerSocketChannelConfig setFreeBind(boolean freeBind) {
+        Native.setIpFreeBind(channel.fd().intValue(), freeBind ? 1: 0);
+        return this;
+    }
 }
diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java
index ede80dcef6c..492d3f86804 100644
--- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java
+++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java
@@ -635,6 +635,7 @@ public static void shutdown(int fd, boolean read, boolean write) throws IOExcept
     public static native int getTcpKeepIntvl(int fd);
     public static native int getTcpKeepCnt(int fd);
     public static native int getSoError(int fd);
+    public static native int isIpFreeBind(int fd);
 
     public static native void setKeepAlive(int fd, int keepAlive);
     public static native void setReceiveBufferSize(int fd, int receiveBufferSize);
@@ -650,7 +651,7 @@ public static void shutdown(int fd, boolean read, boolean write) throws IOExcept
     public static native void setTcpKeepIdle(int fd, int seconds);
     public static native void setTcpKeepIntvl(int fd, int seconds);
     public static native void setTcpKeepCnt(int fd, int probes);
-
+    public static native void setIpFreeBind(int fd, int freeBind);
     public static void tcpInfo(int fd, EpollTcpInfo info) {
         tcpInfo0(fd, info.info);
     }


Test patch: