selftests: mptcp: add TCP_INQ support
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>
This commit is contained in:
parent
2c9e77659a
commit
5cbd886ce2
2 changed files with 61 additions and 3 deletions
tools/testing/selftests/net/mptcp
|
@ -73,12 +73,20 @@ static uint32_t cfg_mark;
|
||||||
struct cfg_cmsg_types {
|
struct cfg_cmsg_types {
|
||||||
unsigned int cmsg_enabled:1;
|
unsigned int cmsg_enabled:1;
|
||||||
unsigned int timestampns:1;
|
unsigned int timestampns:1;
|
||||||
|
unsigned int tcp_inq:1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cfg_sockopt_types {
|
struct cfg_sockopt_types {
|
||||||
unsigned int transparent:1;
|
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_cmsg_types cfg_cmsg_types;
|
||||||
static struct cfg_sockopt_types cfg_sockopt_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)
|
static void process_cmsg(struct msghdr *msgh)
|
||||||
{
|
{
|
||||||
struct __kernel_timespec ts;
|
struct __kernel_timespec ts;
|
||||||
|
bool inq_found = false;
|
||||||
bool ts_found = false;
|
bool ts_found = false;
|
||||||
|
unsigned int inq = 0;
|
||||||
struct cmsghdr *cmsg;
|
struct cmsghdr *cmsg;
|
||||||
|
|
||||||
for (cmsg = CMSG_FIRSTHDR(msgh); cmsg ; cmsg = CMSG_NXTHDR(msgh, 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;
|
ts_found = true;
|
||||||
continue;
|
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 (cfg_cmsg_types.timestampns) {
|
||||||
if (!ts_found)
|
if (!ts_found)
|
||||||
xerror("TIMESTAMPNS not present\n");
|
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)
|
static ssize_t do_recvmsg_cmsg(const int fd, char *buf, const size_t len)
|
||||||
|
@ -420,11 +445,24 @@ static ssize_t do_recvmsg_cmsg(const int fd, char *buf, const size_t len)
|
||||||
.msg_controllen = sizeof(msg_buf),
|
.msg_controllen = sizeof(msg_buf),
|
||||||
};
|
};
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
|
unsigned int last_hint = tcp_inq.last;
|
||||||
int ret = recvmsg(fd, &msg, flags);
|
int ret = recvmsg(fd, &msg, flags);
|
||||||
|
|
||||||
if (ret <= 0)
|
if (ret <= 0) {
|
||||||
|
if (ret == 0 && tcp_inq.expect_eof)
|
||||||
return ret;
|
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)
|
if (msg.msg_controllen && !cfg_cmsg_types.cmsg_enabled)
|
||||||
xerror("got %lu bytes of cmsg data, expected 0\n",
|
xerror("got %lu bytes of cmsg data, expected 0\n",
|
||||||
(unsigned long)msg.msg_controllen);
|
(unsigned long)msg.msg_controllen);
|
||||||
|
@ -435,6 +473,19 @@ static ssize_t do_recvmsg_cmsg(const int fd, char *buf, const size_t len)
|
||||||
if (msg.msg_controllen)
|
if (msg.msg_controllen)
|
||||||
process_cmsg(&msg);
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -944,6 +995,8 @@ static void apply_cmsg_types(int fd, const struct cfg_cmsg_types *cmsg)
|
||||||
|
|
||||||
if (cmsg->timestampns)
|
if (cmsg->timestampns)
|
||||||
xsetsockopt(fd, SOL_SOCKET, SO_TIMESTAMPNS_NEW, &on, sizeof(on));
|
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)
|
static void parse_cmsg_types(const char *type)
|
||||||
|
@ -965,6 +1018,11 @@ static void parse_cmsg_types(const char *type)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strncmp(type, "TCPINQ", len) == 0) {
|
||||||
|
cfg_cmsg_types.tcp_inq = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
fprintf(stderr, "Unrecognized cmsg option %s\n", type);
|
fprintf(stderr, "Unrecognized cmsg option %s\n", type);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,7 +178,7 @@ do_transfer()
|
||||||
|
|
||||||
timeout ${timeout_test} \
|
timeout ${timeout_test} \
|
||||||
ip netns exec ${listener_ns} \
|
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" &
|
${local_addr} < "$sin" > "$sout" &
|
||||||
spid=$!
|
spid=$!
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ do_transfer()
|
||||||
|
|
||||||
timeout ${timeout_test} \
|
timeout ${timeout_test} \
|
||||||
ip netns exec ${connector_ns} \
|
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" &
|
$connect_addr < "$cin" > "$cout" &
|
||||||
|
|
||||||
cpid=$!
|
cpid=$!
|
||||||
|
|
Loading…
Add table
Reference in a new issue