summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2020-06-19 21:07:09 +0000
committerStefan Metzmacher <metze@samba.org>2020-06-19 21:07:09 +0000
commitf4fc52be79edc74bd24b3ee968c5358af81b17bd (patch)
treeb9004de1af9a4e645da631f6ebf905967207d9c6
parentbbc1d654cae9197fa3782e8ea8e1aaa9fdf0cfb0 (diff)
parentf317ebcdcdd626ed9e06de2eb60031306994c803 (diff)
downloadsocket_wrapper-f4fc52be79edc74bd24b3ee968c5358af81b17bd.tar.gz
socket_wrapper-f4fc52be79edc74bd24b3ee968c5358af81b17bd.tar.xz
socket_wrapper-f4fc52be79edc74bd24b3ee968c5358af81b17bd.zip
Merge branch 'tcp_info' into 'master'
Add support for TCP_INFO and SIOCOUTQ/TIOCOUTQ/FIONWRITE See merge request cwrap/socket_wrapper!11
-rw-r--r--ConfigureChecks.cmake1
-rw-r--r--config.h.cmake1
-rw-r--r--src/socket_wrapper.c83
-rw-r--r--tests/echo_srv.c32
-rw-r--r--tests/test_echo_tcp_socket_options.c50
5 files changed, 164 insertions, 3 deletions
diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake
index 4d5adc4..4a2f55e 100644
--- a/ConfigureChecks.cmake
+++ b/ConfigureChecks.cmake
@@ -43,6 +43,7 @@ int main(void){ return 0; }
endif(CMAKE_COMPILER_IS_GNUCC AND NOT MINGW AND NOT OS2)
# HEADERS
+check_include_file(netinet/tcp_fsm.h HAVE_NETINET_TCP_FSM_H)
check_include_file(sys/filio.h HAVE_SYS_FILIO_H)
check_include_file(sys/signalfd.h HAVE_SYS_SIGNALFD_H)
check_include_file(sys/eventfd.h HAVE_SYS_EVENTFD_H)
diff --git a/config.h.cmake b/config.h.cmake
index 36050b5..d3ceb23 100644
--- a/config.h.cmake
+++ b/config.h.cmake
@@ -9,6 +9,7 @@
/************************** HEADER FILES *************************/
+#cmakedefine HAVE_NETINET_TCP_FSM_H 1
#cmakedefine HAVE_SYS_FILIO_H 1
#cmakedefine HAVE_SYS_SIGNALFD_H 1
#cmakedefine HAVE_SYS_EVENTFD_H 1
diff --git a/src/socket_wrapper.c b/src/socket_wrapper.c
index 5b7c9ea..333bdd4 100644
--- a/src/socket_wrapper.c
+++ b/src/socket_wrapper.c
@@ -66,6 +66,9 @@
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
+#ifdef HAVE_NETINET_TCP_FSM_H
+#include <netinet/tcp_fsm.h>
+#endif
#include <arpa/inet.h>
#include <fcntl.h>
#include <stdlib.h>
@@ -264,6 +267,7 @@ struct socket_info
int defer_connect;
int pktinfo;
int tcp_nodelay;
+ int listening;
/* The unix path so we can unlink it on close() */
struct sockaddr_un un_addr;
@@ -4097,6 +4101,9 @@ static int swrap_listen(int s, int backlog)
}
ret = libc_listen(s, backlog);
+ if (ret == 0) {
+ si->listening = 1;
+ }
out:
SWRAP_UNLOCK_SI(si);
@@ -4446,6 +4453,56 @@ static int swrap_getsockopt(int s, int level, int optname,
ret = 0;
goto done;
#endif /* TCP_NODELAY */
+#ifdef TCP_INFO
+ case TCP_INFO: {
+ struct tcp_info info;
+ socklen_t ilen = sizeof(info);
+
+#ifdef HAVE_NETINET_TCP_FSM_H
+/* This is FreeBSD */
+# define __TCP_LISTEN TCPS_LISTEN
+# define __TCP_ESTABLISHED TCPS_ESTABLISHED
+# define __TCP_CLOSE TCPS_CLOSED
+#else
+/* This is Linux */
+# define __TCP_LISTEN TCP_LISTEN
+# define __TCP_ESTABLISHED TCP_ESTABLISHED
+# define __TCP_CLOSE TCP_CLOSE
+#endif
+
+ ZERO_STRUCT(info);
+ if (si->listening) {
+ info.tcpi_state = __TCP_LISTEN;
+ } else if (si->connected) {
+ /*
+ * For now we just fake a few values
+ * supported both by FreeBSD and Linux
+ */
+ info.tcpi_state = __TCP_ESTABLISHED;
+ info.tcpi_rto = 200000; /* 200 msec */
+ info.tcpi_rtt = 5000; /* 5 msec */
+ info.tcpi_rttvar = 5000; /* 5 msec */
+ } else {
+ info.tcpi_state = __TCP_CLOSE;
+ info.tcpi_rto = 1000000; /* 1 sec */
+ info.tcpi_rtt = 0;
+ info.tcpi_rttvar = 250000; /* 250 msec */
+ }
+
+ if (optval == NULL || optlen == NULL ||
+ *optlen < (socklen_t)ilen) {
+ errno = EINVAL;
+ ret = -1;
+ goto done;
+ }
+
+ *optlen = ilen;
+ memcpy(optval, &info, ilen);
+
+ ret = 0;
+ goto done;
+ }
+#endif /* TCP_INFO */
default:
break;
}
@@ -4578,7 +4635,7 @@ static int swrap_vioctl(int s, unsigned long int r, va_list va)
{
struct socket_info *si = find_socket_info(s);
va_list ap;
- int value;
+ int *value_ptr = NULL;
int rc;
if (!si) {
@@ -4593,14 +4650,34 @@ static int swrap_vioctl(int s, unsigned long int r, va_list va)
switch (r) {
case FIONREAD:
- value = *((int *)va_arg(ap, int *));
+ if (rc == 0) {
+ value_ptr = ((int *)va_arg(ap, int *));
+ }
if (rc == -1 && errno != EAGAIN && errno != ENOBUFS) {
swrap_pcap_dump_packet(si, NULL, SWRAP_PENDING_RST, NULL, 0);
- } else if (value == 0) { /* END OF FILE */
+ } else if (value_ptr != NULL && *value_ptr == 0) { /* END OF FILE */
swrap_pcap_dump_packet(si, NULL, SWRAP_PENDING_RST, NULL, 0);
}
break;
+#ifdef FIONWRITE
+ case FIONWRITE:
+ /* this is FreeBSD */
+ FALL_THROUGH; /* to TIOCOUTQ */
+#endif /* FIONWRITE */
+ case TIOCOUTQ: /* same as SIOCOUTQ on Linux */
+ /*
+ * This may return more bytes then the application
+ * sent into the socket, for tcp it should
+ * return the number of unacked bytes.
+ *
+ * On AF_UNIX, all bytes are immediately acked!
+ */
+ if (rc == 0) {
+ value_ptr = ((int *)va_arg(ap, int *));
+ *value_ptr = 0;
+ }
+ break;
}
va_end(ap);
diff --git a/tests/echo_srv.c b/tests/echo_srv.c
index 93778f2..87c85f7 100644
--- a/tests/echo_srv.c
+++ b/tests/echo_srv.c
@@ -9,6 +9,10 @@
#include <arpa/inet.h>
#include <netinet/in.h>
+#include <netinet/tcp.h>
+#ifdef HAVE_NETINET_TCP_FSM_H
+#include <netinet/tcp_fsm.h>
+#endif
#include <netdb.h>
#include <resolv.h>
@@ -315,6 +319,34 @@ static int setup_srv(struct echo_srv_opts *opts, int *_sock)
perror("listen");
return ret;
}
+#ifdef TCP_INFO
+ {
+ struct tcp_info info;
+ socklen_t optlen = sizeof(info);
+
+ ZERO_STRUCT(info);
+ ret = getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, &optlen);
+ if (ret == -1) {
+ ret = errno;
+ perror("TCP_INFO failed");
+ close(sock);
+ return ret;
+ }
+#ifdef HAVE_NETINET_TCP_FSM_H
+/* This is FreeBSD */
+# define __TCP_LISTEN TCPS_LISTEN
+#else
+/* This is Linux */
+# define __TCP_LISTEN TCP_LISTEN
+#endif
+ if (info.tcpi_state != __TCP_LISTEN) {
+ errno = ret = ERANGE;
+ perror("not __TCP_LISTEN => ERANGE...");
+ close(sock);
+ return ret;
+ }
+ }
+#endif /* TCP_INFO */
}
*_sock = sock;
diff --git a/tests/test_echo_tcp_socket_options.c b/tests/test_echo_tcp_socket_options.c
index dfa46fe..5e5661d 100644
--- a/tests/test_echo_tcp_socket_options.c
+++ b/tests/test_echo_tcp_socket_options.c
@@ -11,12 +11,25 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
+#ifdef HAVE_NETINET_TCP_FSM_H
+#include <netinet/tcp_fsm.h>
+#endif
#include <arpa/inet.h>
#include <netdb.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
+#ifdef HAVE_NETINET_TCP_FSM_H
+/* This is FreeBSD */
+# define __TCP_ESTABLISHED TCPS_ESTABLISHED
+# define __TCP_CLOSE TCPS_CLOSED
+#else
+/* This is Linux */
+# define __TCP_ESTABLISHED TCP_ESTABLISHED
+# define __TCP_CLOSE TCP_CLOSE
+#endif
+
#ifndef ZERO_STRUCT
#define ZERO_STRUCT(x) memset((char *)&(x), 0, sizeof(x))
#endif
@@ -300,6 +313,9 @@ static void test_sockopt_tcp(void **state)
.sa_socklen = sizeof(struct sockaddr_in),
};
int opt = -1;
+#ifdef TCP_INFO
+ struct tcp_info info;
+#endif
socklen_t optlen = sizeof(int);
int rc;
@@ -318,9 +334,27 @@ static void test_sockopt_tcp(void **state)
&addr.sa.in.sin_addr);
assert_int_equal(rc, 1);
+#ifdef TCP_INFO
+ ZERO_STRUCT(info);
+ optlen = sizeof(info);
+ rc = getsockopt(s, IPPROTO_TCP, TCP_INFO, &info, &optlen);
+ assert_return_code(rc, errno);
+ assert_int_equal(optlen, sizeof(info));
+ printf("info.tcpi_state=0x%x\n", info.tcpi_state);
+ printf("info.tcpi_rto=%u\n", info.tcpi_rto);
+ printf("info.tcpi_rtt=%u\n", info.tcpi_rtt);
+ printf("info.tcpi_rttvar=%u\n", info.tcpi_rttvar);
+ assert_int_equal(info.tcpi_state, __TCP_CLOSE);
+ assert_int_not_equal(info.tcpi_rto, 0);
+ assert_int_equal(info.tcpi_rtt, 0);
+ assert_int_not_equal(info.tcpi_rttvar, 0);
+#endif /* TCP_INFO */
+
rc = connect(s, &addr.sa.s, addr.sa_socklen);
assert_int_equal(rc, 0);
+ opt = -1;
+ optlen = sizeof(int);
rc = getsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen);
assert_return_code(rc, errno);
assert_int_equal(opt, 0);
@@ -336,6 +370,22 @@ static void test_sockopt_tcp(void **state)
assert_return_code(rc, errno);
assert_int_equal(opt, 1);
+#ifdef TCP_INFO
+ ZERO_STRUCT(info);
+ optlen = sizeof(info);
+ rc = getsockopt(s, IPPROTO_TCP, TCP_INFO, &info, &optlen);
+ assert_return_code(rc, errno);
+ assert_int_equal(optlen, sizeof(info));
+ printf("info.tcpi_state=0x%x\n", info.tcpi_state);
+ printf("info.tcpi_rto=%u\n", info.tcpi_rto);
+ printf("info.tcpi_rtt=%u\n", info.tcpi_rtt);
+ printf("info.tcpi_rttvar=%u\n", info.tcpi_rttvar);
+ assert_int_equal(info.tcpi_state, __TCP_ESTABLISHED);
+ assert_int_not_equal(info.tcpi_rto, 0);
+ assert_int_not_equal(info.tcpi_rtt, 0);
+ assert_int_not_equal(info.tcpi_rttvar, 0);
+#endif /* TCP_INFO */
+
close(s);
}