Repository: netty/netty
Issue: Add support for TCP_INFO in native transport
Labels: feature

It would be nice to support TCP_INFO in the native transport to get infos about the socket.


Issue comments:
See http://linux.die.net/man/7/tcp

Sounds awesome.

@normanmaurer would you log them or so?

@trustin working on it...

@daschl nope just expose it on the EpollSocketChannel. You can do whatever you want to do with it


Pull request review comments:
Because all the fields of TCP_INFO are unsigned, we should use wider types - int for u8, int for u16, long for u32.

good idea.

@trustin do we want to use byte for all u8 methods ? or better stick with int ?

Should be short or int.

Consistency: `JNIEnv* env`


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 137187977de..3aa984559e6 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
@@ -1297,6 +1297,48 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepCnt(JNIEnv*
      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, SOL_TCP, TCP_INFO, &tcp_info, sizeof(tcp_info)) == -1) {
+         return;
+     }
+     unsigned int cArray[32];
+     cArray[0] = tcp_info.tcpi_state;
+     cArray[1] = tcp_info.tcpi_ca_state;
+     cArray[2] = tcp_info.tcpi_retransmits;
+     cArray[3] = tcp_info.tcpi_probes;
+     cArray[4] = tcp_info.tcpi_backoff;
+     cArray[5] = tcp_info.tcpi_options;
+     cArray[6] = tcp_info.tcpi_snd_wscale;
+     cArray[7] = tcp_info.tcpi_rcv_wscale;
+     cArray[8] = tcp_info.tcpi_rto;
+     cArray[9] = tcp_info.tcpi_ato;
+     cArray[10] = tcp_info.tcpi_snd_mss;
+     cArray[11] = tcp_info.tcpi_rcv_mss;
+     cArray[12] = tcp_info.tcpi_unacked;
+     cArray[13] = tcp_info.tcpi_sacked;
+     cArray[14] = tcp_info.tcpi_lost;
+     cArray[15] = tcp_info.tcpi_retrans;
+     cArray[16] = tcp_info.tcpi_fackets;
+     cArray[17] = tcp_info.tcpi_last_data_sent;
+     cArray[18] = tcp_info.tcpi_last_ack_sent;
+     cArray[19] = tcp_info.tcpi_last_data_recv;
+     cArray[20] = tcp_info.tcpi_last_ack_recv;
+     cArray[21] = tcp_info.tcpi_pmtu;
+     cArray[22] = tcp_info.tcpi_rcv_ssthresh;
+     cArray[23] = tcp_info.tcpi_rtt;
+     cArray[24] = tcp_info.tcpi_rttvar;
+     cArray[25] = tcp_info.tcpi_snd_ssthresh;
+     cArray[26] = tcp_info.tcpi_snd_cwnd;
+     cArray[27] = tcp_info.tcpi_advmss;
+     cArray[28] = tcp_info.tcpi_reordering;
+     cArray[29] = tcp_info.tcpi_rcv_rtt;
+     cArray[30] = tcp_info.tcpi_rcv_space;
+     cArray[31] = tcp_info.tcpi_total_retrans;
+
+     (*env)->SetIntArrayRegion(env, array, 0, 32, cArray);
+}
+
 JNIEXPORT jstring JNICALL Java_io_netty_channel_epoll_Native_kernelVersion(JNIEnv* env, jclass clazz) {
     struct utsname name;
 
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 58e82bfb8f1..7c0eaafdca7 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
@@ -38,75 +38,81 @@
 #define UIO_MAXIOV 1024
 #endif /* UIO_MAXIOV */
 
-jint Java_io_netty_channel_epoll_Native_eventFd(JNIEnv * env, jclass clazz);
-void Java_io_netty_channel_epoll_Native_eventFdWrite(JNIEnv * env, jclass clazz, jint fd, jlong value);
-void Java_io_netty_channel_epoll_Native_eventFdRead(JNIEnv * env, jclass clazz, jint fd);
-jint Java_io_netty_channel_epoll_Native_epollCreate(JNIEnv * env, jclass clazz);
-jint Java_io_netty_channel_epoll_Native_epollWait(JNIEnv * env, jclass clazz, jint efd, jlongArray events, jint timeout);
-void Java_io_netty_channel_epoll_Native_epollCtlAdd(JNIEnv * env, jclass clazz, jint efd, jint fd, jint flags, jint id);
-void Java_io_netty_channel_epoll_Native_epollCtlMod(JNIEnv * env, jclass clazz, jint efd, jint fd, jint flags, jint id);
-void  Java_io_netty_channel_epoll_Native_epollCtlDel(JNIEnv * env, jclass clazz, jint efd, jint fd);
-jint Java_io_netty_channel_epoll_Native_write0(JNIEnv * env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit);
-jint Java_io_netty_channel_epoll_Native_writeAddress0(JNIEnv * env, jclass clazz, jint fd, jlong address, jint pos, jint limit);
-jlong Java_io_netty_channel_epoll_Native_writev0(JNIEnv * env, jclass clazz, jint fd, jobjectArray buffers, jint offset, jint length);
-jlong Java_io_netty_channel_epoll_Native_writevAddresses0(JNIEnv * env, jclass clazz, jint fd, jlong memoryAddress, jint length);
-jint Java_io_netty_channel_epoll_Native_sendTo(JNIEnv * env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port);
-jint Java_io_netty_channel_epoll_Native_sendToAddress(JNIEnv * env, jclass clazz, jint fd, jlong memoryAddress, jint pos, jint limit, jbyteArray address, jint scopeId, jint port);
-jint Java_io_netty_channel_epoll_Native_sendToAddresses(JNIEnv * env, jclass clazz, jint fd, jlong memoryAddress, jint length, jbyteArray address, jint scopeId, jint port);
-jint Java_io_netty_channel_epoll_Native_sendmmsg(JNIEnv * env, jclass clazz, jint fd, jobjectArray packets, jint offset, jint len);
+jint Java_io_netty_channel_epoll_Native_eventFd(JNIEnv* env, jclass clazz);
+void Java_io_netty_channel_epoll_Native_eventFdWrite(JNIEnv* env, jclass clazz, jint fd, jlong value);
+void Java_io_netty_channel_epoll_Native_eventFdRead(JNIEnv* env, jclass clazz, jint fd);
+jint Java_io_netty_channel_epoll_Native_epollCreate(JNIEnv* env, jclass clazz);
+jint Java_io_netty_channel_epoll_Native_epollWait(JNIEnv* env, jclass clazz, jint efd, jlongArray events, jint timeout);
+void Java_io_netty_channel_epoll_Native_epollCtlAdd(JNIEnv* env, jclass clazz, jint efd, jint fd, jint flags, jint id);
+void Java_io_netty_channel_epoll_Native_epollCtlMod(JNIEnv* env, jclass clazz, jint efd, jint fd, jint flags, jint id);
+void  Java_io_netty_channel_epoll_Native_epollCtlDel(JNIEnv* env, jclass clazz, jint efd, jint fd);
+jint Java_io_netty_channel_epoll_Native_write0(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit);
+jint Java_io_netty_channel_epoll_Native_writeAddress0(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit);
+jlong Java_io_netty_channel_epoll_Native_writev0(JNIEnv* env, jclass clazz, jint fd, jobjectArray buffers, jint offset, jint length);
+jlong Java_io_netty_channel_epoll_Native_writevAddresses0(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length);
+jint Java_io_netty_channel_epoll_Native_sendTo(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port);
+jint Java_io_netty_channel_epoll_Native_sendToAddress(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint pos, jint limit, jbyteArray address, jint scopeId, jint port);
+jint Java_io_netty_channel_epoll_Native_sendToAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length, jbyteArray address, jint scopeId, jint port);
+jint Java_io_netty_channel_epoll_Native_sendmmsg(JNIEnv* env, jclass clazz, jint fd, jobjectArray packets, jint offset, jint len);
 
-jint Java_io_netty_channel_epoll_Native_read0(JNIEnv * env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit);
-jint Java_io_netty_channel_epoll_Native_readAddress0(JNIEnv * env, jclass clazz, jint fd, jlong address, jint pos, jint limit);
-jobject Java_io_netty_channel_epoll_Native_recvFrom(JNIEnv * env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit);
-jobject Java_io_netty_channel_epoll_Native_recvFromAddress(JNIEnv * env, jclass clazz, jint fd, jlong address, jint pos, jint limit);
-jint Java_io_netty_channel_epoll_Native_close0(JNIEnv * env, jclass clazz, jint fd);
-jint Java_io_netty_channel_epoll_Native_shutdown0(JNIEnv * env, jclass clazz, jint fd, jboolean read, jboolean write);
-jint Java_io_netty_channel_epoll_Native_socketStream(JNIEnv * env, jclass clazz);
-jint Java_io_netty_channel_epoll_Native_socketDgram(JNIEnv * env, jclass clazz);
+jint Java_io_netty_channel_epoll_Native_read0(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit);
+jint Java_io_netty_channel_epoll_Native_readAddress0(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit);
+jobject Java_io_netty_channel_epoll_Native_recvFrom(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit);
+jobject Java_io_netty_channel_epoll_Native_recvFromAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit);
+jint Java_io_netty_channel_epoll_Native_close0(JNIEnv* env, jclass clazz, jint fd);
+jint Java_io_netty_channel_epoll_Native_shutdown0(JNIEnv* env, jclass clazz, jint fd, jboolean read, jboolean write);
+jint Java_io_netty_channel_epoll_Native_socketStream(JNIEnv* env, jclass clazz);
+jint Java_io_netty_channel_epoll_Native_socketDgram(JNIEnv* env, jclass clazz);
+jint Java_io_netty_channel_epoll_Native_socketDomain(JNIEnv* env, jclass clazz);
 
-jint Java_io_netty_channel_epoll_Native_bind(JNIEnv * env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port);
-jint Java_io_netty_channel_epoll_Native_listen0(JNIEnv * env, jclass clazz, jint fd, jint backlog);
-jint Java_io_netty_channel_epoll_Native_connect(JNIEnv * env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port);
-jint Java_io_netty_channel_epoll_Native_finishConnect0(JNIEnv * env, jclass clazz, jint fd);
-jint Java_io_netty_channel_epoll_Native_accept0(JNIEnv * env, jclass clazz, jint fd);
-jlong Java_io_netty_channel_epoll_Native_sendfile0(JNIEnv *env, jclass clazz, jint fd, jobject fileRegion, jlong base_off, jlong off, jlong len);
-jbyteArray Java_io_netty_channel_epoll_Native_remoteAddress0(JNIEnv * env, jclass clazz, jint fd);
-jbyteArray Java_io_netty_channel_epoll_Native_localAddress0(JNIEnv * env, jclass clazz, jint fd);
-void Java_io_netty_channel_epoll_Native_setReuseAddress(JNIEnv * env, jclass clazz, jint fd, jint optval);
-void Java_io_netty_channel_epoll_Native_setReusePort(JNIEnv * env, jclass clazz, jint fd, jint optval);
-void Java_io_netty_channel_epoll_Native_setTcpNoDelay(JNIEnv *env, jclass clazz, jint fd, jint optval);
-void Java_io_netty_channel_epoll_Native_setReceiveBufferSize(JNIEnv *env, jclass clazz, jint fd, jint optval);
-void Java_io_netty_channel_epoll_Native_setSendBufferSize(JNIEnv *env, jclass clazz, jint fd, jint optval);
-void Java_io_netty_channel_epoll_Native_setKeepAlive(JNIEnv *env, jclass clazz, jint fd, jint optval);
-void Java_io_netty_channel_epoll_Native_setTcpCork(JNIEnv *env, jclass clazz, jint fd, jint optval);
-void Java_io_netty_channel_epoll_Native_setSoLinger(JNIEnv *env, jclass clazz, jint fd, jint optval);
-void Java_io_netty_channel_epoll_Native_setTrafficClass(JNIEnv *env, jclass clazz, jint fd, jint optval);
-void Java_io_netty_channel_epoll_Native_setBroadcast(JNIEnv *env, jclass clazz, jint fd, jint optval);
-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);
+jint Java_io_netty_channel_epoll_Native_bind(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port);
+jint Java_io_netty_channel_epoll_Native_bindDomainSocket(JNIEnv* env, jclass clazz, jint fd, jstring address);
+jint Java_io_netty_channel_epoll_Native_recvFd0(JNIEnv* env, jclass clazz, jint fd);
+jint Java_io_netty_channel_epoll_Native_sendFd0(JNIEnv* env, jclass clazz, jint socketFd, jint fd);
 
-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);
-jint Java_io_netty_channel_epoll_Native_isTcpNoDelay(JNIEnv *env, jclass clazz, jint fd);
-jint Java_io_netty_channel_epoll_Native_getReceiveBufferSize(JNIEnv * env, jclass clazz, jint fd);
-jint Java_io_netty_channel_epoll_Native_getSendBufferSize(JNIEnv *env, jclass clazz, jint fd);
-jint Java_io_netty_channel_epoll_Native_isTcpCork(JNIEnv *env, jclass clazz, jint fd);
-jint Java_io_netty_channel_epoll_Native_getSoLinger(JNIEnv *env, jclass clazz, jint fd);
-jint Java_io_netty_channel_epoll_Native_getTrafficClass(JNIEnv *env, jclass clazz, jint fd);
-jint Java_io_netty_channel_epoll_Native_isBroadcast(JNIEnv *env, jclass clazz, jint fd);
-jint Java_io_netty_channel_epoll_Native_getTcpKeepIdle(JNIEnv *env, jclass clazz, jint fd);
-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_listen0(JNIEnv* env, jclass clazz, jint fd, jint backlog);
+jint Java_io_netty_channel_epoll_Native_connect(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port);
+jint Java_io_netty_channel_epoll_Native_connectDomainSocket(JNIEnv* env, jclass clazz, jint fd, jstring address);
+jint Java_io_netty_channel_epoll_Native_finishConnect0(JNIEnv* env, jclass clazz, jint fd);
+jint Java_io_netty_channel_epoll_Native_accept0(JNIEnv* env, jclass clazz, jint fd);
+jlong Java_io_netty_channel_epoll_Native_sendfile0(JNIEnv* env, jclass clazz, jint fd, jobject fileRegion, jlong base_off, jlong off, jlong len);
+jbyteArray Java_io_netty_channel_epoll_Native_remoteAddress0(JNIEnv* env, jclass clazz, jint fd);
+jbyteArray Java_io_netty_channel_epoll_Native_localAddress0(JNIEnv* env, jclass clazz, jint fd);
+void Java_io_netty_channel_epoll_Native_setReuseAddress(JNIEnv* env, jclass clazz, jint fd, jint optval);
+void Java_io_netty_channel_epoll_Native_setReusePort(JNIEnv* env, jclass clazz, jint fd, jint optval);
+void Java_io_netty_channel_epoll_Native_setTcpNoDelay(JNIEnv* env, jclass clazz, jint fd, jint optval);
+void Java_io_netty_channel_epoll_Native_setReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval);
+void Java_io_netty_channel_epoll_Native_setSendBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval);
+void Java_io_netty_channel_epoll_Native_setKeepAlive(JNIEnv* env, jclass clazz, jint fd, jint optval);
+void Java_io_netty_channel_epoll_Native_setTcpCork(JNIEnv* env, jclass clazz, jint fd, jint optval);
+void Java_io_netty_channel_epoll_Native_setSoLinger(JNIEnv* env, jclass clazz, jint fd, jint optval);
+void Java_io_netty_channel_epoll_Native_setTrafficClass(JNIEnv* env, jclass clazz, jint fd, jint optval);
+void Java_io_netty_channel_epoll_Native_setBroadcast(JNIEnv* env, jclass clazz, jint fd, jint optval);
+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);
 
-jstring Java_io_netty_channel_epoll_Native_kernelVersion(JNIEnv *env, jclass clazz);
-jint Java_io_netty_channel_epoll_Native_iovMax(JNIEnv *env, jclass clazz);
-jint Java_io_netty_channel_epoll_Native_uioMaxIov(JNIEnv *env, jclass clazz);
-jboolean Java_io_netty_channel_epoll_Native_isSupportingSendmmsg(JNIEnv *env, jclass clazz);
+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);
+jint Java_io_netty_channel_epoll_Native_isTcpNoDelay(JNIEnv* env, jclass clazz, jint fd);
+jint Java_io_netty_channel_epoll_Native_getReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd);
+jint Java_io_netty_channel_epoll_Native_getSendBufferSize(JNIEnv* env, jclass clazz, jint fd);
+jint Java_io_netty_channel_epoll_Native_isTcpCork(JNIEnv* env, jclass clazz, jint fd);
+jint Java_io_netty_channel_epoll_Native_getSoLinger(JNIEnv* env, jclass clazz, jint fd);
+jint Java_io_netty_channel_epoll_Native_getTrafficClass(JNIEnv* env, jclass clazz, jint fd);
+jint Java_io_netty_channel_epoll_Native_isBroadcast(JNIEnv* env, jclass clazz, jint fd);
+jint Java_io_netty_channel_epoll_Native_getTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd);
+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_errnoEBADF(JNIEnv *env, jclass clazz);
-jint Java_io_netty_channel_epoll_Native_errnoEPIPE(JNIEnv *env, jclass clazz);
-jint Java_io_netty_channel_epoll_Native_errnoEAGAIN(JNIEnv *env, jclass clazz);
-jint Java_io_netty_channel_epoll_Native_errnoEWOULDBLOCK(JNIEnv *env, jclass clazz);
-jint Java_io_netty_channel_epoll_Native_errnoEINPROGRESS(JNIEnv *env, jclass clazz);
-jstring Java_io_netty_channel_epoll_Native_strError(JNIEnv *env, jclass clazz, jint err);
+jstring Java_io_netty_channel_epoll_Native_kernelVersion(JNIEnv* env, jclass clazz);
+jint Java_io_netty_channel_epoll_Native_iovMax(JNIEnv* env, jclass clazz);
+jint Java_io_netty_channel_epoll_Native_uioMaxIov(JNIEnv* env, jclass clazz);
+jboolean Java_io_netty_channel_epoll_Native_isSupportingSendmmsg(JNIEnv* env, jclass clazz);
+
+jint Java_io_netty_channel_epoll_Native_errnoEBADF(JNIEnv* env, jclass clazz);
+jint Java_io_netty_channel_epoll_Native_errnoEPIPE(JNIEnv* env, jclass clazz);
+jint Java_io_netty_channel_epoll_Native_errnoEAGAIN(JNIEnv* env, jclass clazz);
+jint Java_io_netty_channel_epoll_Native_errnoEWOULDBLOCK(JNIEnv* env, jclass clazz);
+jint Java_io_netty_channel_epoll_Native_errnoEINPROGRESS(JNIEnv* env, jclass clazz);
+jstring Java_io_netty_channel_epoll_Native_strError(JNIEnv* env, jclass clazz, jint err);
diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannel.java
index bfe1f528698..0d81dbff7d4 100644
--- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannel.java
+++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannel.java
@@ -82,6 +82,22 @@ public EpollSocketChannel() {
         config = new EpollSocketChannelConfig(this);
     }
 
+    /**
+     * Returns the {@code TCP_INFO} for the current socket. See <a href="http://linux.die.net/man/7/tcp">man 7 tcp</a>.
+     */
+    public EpollTcpInfo tcpInfo() {
+        return tcpInfo(new EpollTcpInfo());
+    }
+
+    /**
+     * Updates and returns the {@code TCP_INFO} for the current socket.
+     * See <a href="http://linux.die.net/man/7/tcp">man 7 tcp</a>.
+     */
+    public EpollTcpInfo tcpInfo(EpollTcpInfo info) {
+        Native.tcpInfo(fd, info);
+        return info;
+    }
+
     @Override
     protected AbstractEpollUnsafe newUnsafe() {
         return new EpollSocketUnsafe();
diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollTcpInfo.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollTcpInfo.java
new file mode 100644
index 00000000000..9b47a19c52b
--- /dev/null
+++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollTcpInfo.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2014 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.channel.epoll;
+
+/**
+ * <p>
+ * struct tcp_info
+ * {
+ *      __u8    tcpi_state;
+ *      __u8    tcpi_ca_state;
+ *      __u8    tcpi_retransmits;
+ *      __u8    tcpi_probes;
+ *      __u8    tcpi_backoff;
+ *      __u8    tcpi_options;
+ *      __u8    tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4;
+ *
+ *      __u32   tcpi_rto;
+ *      __u32   tcpi_ato;
+ *      __u32   tcpi_snd_mss;
+ *      __u32   tcpi_rcv_mss;
+ *
+ *      __u32   tcpi_unacked;
+ *      __u32   tcpi_sacked;
+ *      __u32   tcpi_lost;
+ *      __u32   tcpi_retrans;
+ *      __u32   tcpi_fackets;
+ *
+ *      __u32   tcpi_last_data_sent;
+ *      __u32   tcpi_last_ack_sent;
+ *      __u32   tcpi_last_data_recv;
+ *      __u32   tcpi_last_ack_recv;
+ *
+ *      __u32   tcpi_pmtu;
+ *      __u32   tcpi_rcv_ssthresh;
+ *      __u32   tcpi_rtt;
+ *      __u32   tcpi_rttvar;
+ *      __u32   tcpi_snd_ssthresh;
+ *      __u32   tcpi_snd_cwnd;
+ *      __u32   tcpi_advmss;
+ *      __u32   tcpi_reordering;
+ *
+ *      __u32   tcpi_rcv_rtt;
+ *      __u32   tcpi_rcv_space;
+ *
+ *      __u32   tcpi_total_retrans;
+ * };
+ * </p>
+ */
+public final class EpollTcpInfo {
+
+    final int[] info = new int[32];
+
+    public int state() {
+        return info[0] & 0xFF;
+    }
+
+    public int caState() {
+        return info[1] & 0xFF;
+    }
+
+    public int retransmits() {
+        return info[2] & 0xFF;
+    }
+
+    public int probes() {
+        return info[3] & 0xFF;
+    }
+
+    public int backoff() {
+        return info[4] & 0xFF;
+    }
+
+    public int options() {
+        return info[5] & 0xFF;
+    }
+
+    public int sndWscale() {
+        return info[6] & 0xFF;
+    }
+
+    public int rcvWscale() {
+        return info[7] & 0xFF;
+    }
+
+    public long rto() {
+        return info[8] & 0xFFFFFFFFL;
+    }
+
+    public long ato() {
+        return info[9] & 0xFFFFFFFFL;
+    }
+
+    public long sndMss() {
+        return info[10] & 0xFFFFFFFFL;
+    }
+
+    public long rcvMss() {
+        return info[11] & 0xFFFFFFFFL;
+    }
+
+    public long unacked() {
+        return info[12] & 0xFFFFFFFFL;
+    }
+
+    public long sacked() {
+        return info[13] & 0xFFFFFFFFL;
+    }
+
+    public long lost() {
+        return info[14] & 0xFFFFFFFFL;
+    }
+
+    public long retrans() {
+        return info[15] & 0xFFFFFFFFL;
+    }
+
+    public long fackets() {
+        return info[16] & 0xFFFFFFFFL;
+    }
+
+    public long lastDataSent() {
+        return info[17] & 0xFFFFFFFFL;
+    }
+
+    public long lastAckSent() {
+        return info[18] & 0xFFFFFFFFL;
+    }
+
+    public long lastDataRecv() {
+        return info[19] & 0xFFFFFFFFL;
+    }
+
+    public long lastAckRecv() {
+        return info[20] & 0xFFFFFFFFL;
+    }
+
+    public long pmtu() {
+        return info[21] & 0xFFFFFFFFL;
+    }
+
+    public long rcvSsthresh() {
+        return info[22] & 0xFFFFFFFFL;
+    }
+
+    public long rtt() {
+        return info[23] & 0xFFFFFFFFL;
+    }
+
+    public long rttvar() {
+        return info[24] & 0xFFFFFFFFL;
+    }
+
+    public long sndSsthresh() {
+        return info[25] & 0xFFFFFFFFL;
+    }
+
+    public long sndCwnd() {
+        return info[26] & 0xFFFFFFFFL;
+    }
+
+    public long advmss() {
+        return info[27] & 0xFFFFFFFFL;
+    }
+
+    public long reordering() {
+        return info[28] & 0xFFFFFFFFL;
+    }
+
+    public long rcvRtt() {
+        return info[29] & 0xFFFFFFFFL;
+    }
+
+    public long rcvSpace() {
+        return info[30] & 0xFFFFFFFFL;
+    }
+
+    public long totalRetrans() {
+        return info[31] & 0xFFFFFFFFL;
+    }
+}
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 4986e3f20b0..46c1903da05 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
@@ -505,6 +505,12 @@ public static void shutdown(int fd, boolean read, boolean write) throws IOExcept
     public static native void setTcpKeepIntvl(int fd, int seconds);
     public static native void setTcpKeepCnt(int fd, int probes);
 
+    public static void tcpInfo(int fd, EpollTcpInfo info) {
+        tcpInfo0(fd, info.info);
+    }
+
+    private static native void tcpInfo0(int fd, int[] array);
+
     private static NativeInetAddress toNativeInetAddress(InetAddress addr) {
         byte[] bytes = addr.getAddress();
         if (addr instanceof Inet6Address) {


Test patch:
diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketChannelTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketChannelTest.java
new file mode 100644
index 00000000000..9637006217e
--- /dev/null
+++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketChannelTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2014 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.channel.epoll;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.EventLoopGroup;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.net.InetSocketAddress;
+
+public class EpollSocketChannelTest {
+
+    @Test
+    public void testTcpInfo() throws Exception {
+        EventLoopGroup group = new EpollEventLoopGroup(1);
+
+        try {
+            Bootstrap bootstrap = new Bootstrap();
+            EpollSocketChannel ch = (EpollSocketChannel) bootstrap.group(group)
+                    .channel(EpollSocketChannel.class)
+                    .handler(new ChannelInboundHandlerAdapter())
+                    .bind(new InetSocketAddress(0)).syncUninterruptibly().channel();
+            EpollTcpInfo info = ch.tcpInfo();
+            assertTcpInfo0(info);
+            ch.close().syncUninterruptibly();
+        } finally {
+            group.shutdownGracefully();
+        }
+    }
+
+    @Test
+    public void testTcpInfoReuse() throws Exception {
+        EventLoopGroup group = new EpollEventLoopGroup(1);
+
+        try {
+            Bootstrap bootstrap = new Bootstrap();
+            EpollSocketChannel ch = (EpollSocketChannel) bootstrap.group(group)
+                    .channel(EpollSocketChannel.class)
+                    .handler(new ChannelInboundHandlerAdapter())
+                    .bind(new InetSocketAddress(0)).syncUninterruptibly().channel();
+            EpollTcpInfo info = new EpollTcpInfo();
+            ch.tcpInfo(info);
+            assertTcpInfo0(info);
+            ch.close().syncUninterruptibly();
+        } finally {
+            group.shutdownGracefully();
+        }
+    }
+
+    private static void assertTcpInfo0(EpollTcpInfo info) throws Exception {
+        Assert.assertNotNull(info);
+
+        Assert.assertTrue(info.state() >= 0);
+        Assert.assertTrue(info.caState() >= 0);
+        Assert.assertTrue(info.retransmits() >= 0);
+        Assert.assertTrue(info.probes() >= 0);
+        Assert.assertTrue(info.backoff() >= 0);
+        Assert.assertTrue(info.options() >= 0);
+        Assert.assertTrue(info.sndWscale() >= 0);
+        Assert.assertTrue(info.rcvWscale() >= 0);
+        Assert.assertTrue(info.rto() >= 0);
+        Assert.assertTrue(info.ato() >= 0);
+        Assert.assertTrue(info.sndMss() >= 0);
+        Assert.assertTrue(info.rcvMss() >= 0);
+        Assert.assertTrue(info.unacked() >= 0);
+        Assert.assertTrue(info.sacked() >= 0);
+        Assert.assertTrue(info.lost() >= 0);
+        Assert.assertTrue(info.retrans() >= 0);
+        Assert.assertTrue(info.fackets() >= 0);
+        Assert.assertTrue(info.lastDataSent() >= 0);
+        Assert.assertTrue(info.lastAckSent() >= 0);
+        Assert.assertTrue(info.lastDataRecv() >= 0);
+        Assert.assertTrue(info.lastAckRecv() >= 0);
+        Assert.assertTrue(info.pmtu() >= 0);
+        Assert.assertTrue(info.rcvSsthresh() >= 0);
+        Assert.assertTrue(info.rtt() >= 0);
+        Assert.assertTrue(info.rttvar() >= 0);
+        Assert.assertTrue(info.sndSsthresh() >= 0);
+        Assert.assertTrue(info.sndCwnd() >= 0);
+        Assert.assertTrue(info.advmss() >= 0);
+        Assert.assertTrue(info.reordering() >= 0);
+        Assert.assertTrue(info.rcvRtt() >= 0);
+        Assert.assertTrue(info.rcvSpace() >= 0);
+        Assert.assertTrue(info.totalRetrans() >= 0);
+    }
+}