selftests: mptcp: add TCP_INQ support
authorFlorian Westphal <fw@strlen.de>
Fri, 3 Dec 2021 22:35:33 +0000 (14:35 -0800)
committerJakub Kicinski <kuba@kernel.org>
Tue, 7 Dec 2021 19:36:29 +0000 (11:36 -0800)
Do checks on the returned inq counter.

Fail on:
1. Huge value (> 1 kbyte, test case files are 1 kb)
2. last hint larger than returned bytes when read was short
3. erronenous indication of EOF.

3) happens when a hint of X bytes reads X-1 on next call
   but next recvmsg returns more data (instead of EOF).

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Mat Martineau <mathew.j.martineau@linux.intel.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
tools/testing/selftests/net/mptcp/mptcp_connect.c
tools/testing/selftests/net/mptcp/mptcp_sockopt.sh

index ada9b80774d439663931c389acc0abb7dbb3dfcf..98de28ac3ba8620905ed0ce7f1d21b3987c618d5 100644 (file)
@@ -73,12 +73,20 @@ static uint32_t cfg_mark;
 struct cfg_cmsg_types {
        unsigned int cmsg_enabled:1;
        unsigned int timestampns:1;
+       unsigned int tcp_inq:1;
 };
 
 struct cfg_sockopt_types {
        unsigned int transparent:1;
 };
 
+struct tcp_inq_state {
+       unsigned int last;
+       bool expect_eof;
+};
+
+static struct tcp_inq_state tcp_inq;
+
 static struct cfg_cmsg_types cfg_cmsg_types;
 static struct cfg_sockopt_types cfg_sockopt_types;
 
@@ -389,7 +397,9 @@ static size_t do_write(const int fd, char *buf, const size_t len)
 static void process_cmsg(struct msghdr *msgh)
 {
        struct __kernel_timespec ts;
+       bool inq_found = false;
        bool ts_found = false;
+       unsigned int inq = 0;
        struct cmsghdr *cmsg;
 
        for (cmsg = CMSG_FIRSTHDR(msgh); cmsg ; cmsg = CMSG_NXTHDR(msgh, cmsg)) {
@@ -398,12 +408,27 @@ static void process_cmsg(struct msghdr *msgh)
                        ts_found = true;
                        continue;
                }
+               if (cmsg->cmsg_level == IPPROTO_TCP && cmsg->cmsg_type == TCP_CM_INQ) {
+                       memcpy(&inq, CMSG_DATA(cmsg), sizeof(inq));
+                       inq_found = true;
+                       continue;
+               }
+
        }
 
        if (cfg_cmsg_types.timestampns) {
                if (!ts_found)
                        xerror("TIMESTAMPNS not present\n");
        }
+
+       if (cfg_cmsg_types.tcp_inq) {
+               if (!inq_found)
+                       xerror("TCP_INQ not present\n");
+
+               if (inq > 1024)
+                       xerror("tcp_inq %u is larger than one kbyte\n", inq);
+               tcp_inq.last = inq;
+       }
 }
 
 static ssize_t do_recvmsg_cmsg(const int fd, char *buf, const size_t len)
@@ -420,10 +445,23 @@ static ssize_t do_recvmsg_cmsg(const int fd, char *buf, const size_t len)
                .msg_controllen = sizeof(msg_buf),
        };
        int flags = 0;
+       unsigned int last_hint = tcp_inq.last;
        int ret = recvmsg(fd, &msg, flags);
 
-       if (ret <= 0)
+       if (ret <= 0) {
+               if (ret == 0 && tcp_inq.expect_eof)
+                       return ret;
+
+               if (ret == 0 && cfg_cmsg_types.tcp_inq)
+                       if (last_hint != 1 && last_hint != 0)
+                               xerror("EOF but last tcp_inq hint was %u\n", last_hint);
+
                return ret;
+       }
+
+       if (tcp_inq.expect_eof)
+               xerror("expected EOF, last_hint %u, now %u\n",
+                      last_hint, tcp_inq.last);
 
        if (msg.msg_controllen && !cfg_cmsg_types.cmsg_enabled)
                xerror("got %lu bytes of cmsg data, expected 0\n",
@@ -435,6 +473,19 @@ static ssize_t do_recvmsg_cmsg(const int fd, char *buf, const size_t len)
        if (msg.msg_controllen)
                process_cmsg(&msg);
 
+       if (cfg_cmsg_types.tcp_inq) {
+               if ((size_t)ret < len && last_hint > (unsigned int)ret) {
+                       if (ret + 1 != (int)last_hint) {
+                               int next = read(fd, msg_buf, sizeof(msg_buf));
+
+                               xerror("read %u of %u, last_hint was %u tcp_inq hint now %u next_read returned %d/%m\n",
+                                      ret, (unsigned int)len, last_hint, tcp_inq.last, next);
+                       } else {
+                               tcp_inq.expect_eof = true;
+                       }
+               }
+       }
+
        return ret;
 }
 
@@ -944,6 +995,8 @@ static void apply_cmsg_types(int fd, const struct cfg_cmsg_types *cmsg)
 
        if (cmsg->timestampns)
                xsetsockopt(fd, SOL_SOCKET, SO_TIMESTAMPNS_NEW, &on, sizeof(on));
+       if (cmsg->tcp_inq)
+               xsetsockopt(fd, IPPROTO_TCP, TCP_INQ, &on, sizeof(on));
 }
 
 static void parse_cmsg_types(const char *type)
@@ -965,6 +1018,11 @@ static void parse_cmsg_types(const char *type)
                return;
        }
 
+       if (strncmp(type, "TCPINQ", len) == 0) {
+               cfg_cmsg_types.tcp_inq = 1;
+               return;
+       }
+
        fprintf(stderr, "Unrecognized cmsg option %s\n", type);
        exit(1);
 }
index 41de643788b80edec74e8cbd1457489ebd134353..c8c364369599e06a1f624eaec007401f39afbfb4 100755 (executable)
@@ -178,7 +178,7 @@ do_transfer()
 
        timeout ${timeout_test} \
                ip netns exec ${listener_ns} \
-                       $mptcp_connect -t ${timeout_poll} -l -M 1 -p $port -s ${srv_proto} -c TIMESTAMPNS \
+                       $mptcp_connect -t ${timeout_poll} -l -M 1 -p $port -s ${srv_proto} -c TIMESTAMPNS,TCPINQ \
                                ${local_addr} < "$sin" > "$sout" &
        spid=$!
 
@@ -186,7 +186,7 @@ do_transfer()
 
        timeout ${timeout_test} \
                ip netns exec ${connector_ns} \
-                       $mptcp_connect -t ${timeout_poll} -M 2 -p $port -s ${cl_proto} -c TIMESTAMPNS \
+                       $mptcp_connect -t ${timeout_poll} -M 2 -p $port -s ${cl_proto} -c TIMESTAMPNS,TCPINQ \
                                $connect_addr < "$cin" > "$cout" &
 
        cpid=$!