selftests: mptcp: add MPTCP_FULL_INFO testcase

Add a testcase explicitly triggering the newly introduce
MPTCP_FULL_INFO getsockopt.

Link: https://github.com/multipath-tcp/mptcp_net-next/issues/388
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Reviewed-by: Matthieu Baerts <matthieu.baerts@tessares.net>
Co-developed-by: Matthieu Baerts <matthieu.baerts@tessares.net>
Signed-off-by: Matthieu Baerts <matthieu.baerts@tessares.net>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Paolo Abeni 2023-06-20 18:30:19 +02:00 committed by Jakub Kicinski
parent 492432074e
commit aa723d5b35
1 changed files with 91 additions and 2 deletions

View File

@ -86,9 +86,38 @@ struct mptcp_subflow_addrs {
#define MPTCP_SUBFLOW_ADDRS 3
#endif
#ifndef MPTCP_FULL_INFO
struct mptcp_subflow_info {
__u32 id;
struct mptcp_subflow_addrs addrs;
};
struct mptcp_full_info {
__u32 size_tcpinfo_kernel; /* must be 0, set by kernel */
__u32 size_tcpinfo_user;
__u32 size_sfinfo_kernel; /* must be 0, set by kernel */
__u32 size_sfinfo_user;
__u32 num_subflows; /* must be 0, set by kernel (real subflow count) */
__u32 size_arrays_user; /* max subflows that userspace is interested in;
* the buffers at subflow_info/tcp_info
* are respectively at least:
* size_arrays * size_sfinfo_user
* size_arrays * size_tcpinfo_user
* bytes wide
*/
__aligned_u64 subflow_info;
__aligned_u64 tcp_info;
struct mptcp_info mptcp_info;
};
#define MPTCP_FULL_INFO 4
#endif
struct so_state {
struct mptcp_info mi;
struct mptcp_info last_sample;
struct tcp_info tcp_info;
struct mptcp_subflow_addrs addrs;
uint64_t mptcpi_rcv_delta;
uint64_t tcpi_rcv_delta;
bool pkt_stats_avail;
@ -370,6 +399,8 @@ static void do_getsockopt_tcp_info(struct so_state *s, int fd, size_t r, size_t
olen -= sizeof(struct mptcp_subflow_data);
assert(olen == ti.d.size_user);
s->tcp_info = ti.ti[0];
if (ti.ti[0].tcpi_bytes_sent == w &&
ti.ti[0].tcpi_bytes_received == r)
goto done;
@ -391,7 +422,7 @@ done:
do_getsockopt_bogus_sf_data(fd, MPTCP_TCPINFO);
}
static void do_getsockopt_subflow_addrs(int fd)
static void do_getsockopt_subflow_addrs(struct so_state *s, int fd)
{
struct sockaddr_storage remote, local;
socklen_t olen, rlen, llen;
@ -439,6 +470,7 @@ static void do_getsockopt_subflow_addrs(int fd)
assert(memcmp(&local, &addrs.addr[0].ss_local, sizeof(local)) == 0);
assert(memcmp(&remote, &addrs.addr[0].ss_remote, sizeof(remote)) == 0);
s->addrs = addrs.addr[0];
memset(&addrs, 0, sizeof(addrs));
@ -459,13 +491,70 @@ static void do_getsockopt_subflow_addrs(int fd)
do_getsockopt_bogus_sf_data(fd, MPTCP_SUBFLOW_ADDRS);
}
static void do_getsockopt_mptcp_full_info(struct so_state *s, int fd)
{
size_t data_size = sizeof(struct mptcp_full_info);
struct mptcp_subflow_info sfinfo[2];
struct tcp_info tcp_info[2];
struct mptcp_full_info mfi;
socklen_t olen;
int ret;
memset(&mfi, 0, data_size);
memset(tcp_info, 0, sizeof(tcp_info));
memset(sfinfo, 0, sizeof(sfinfo));
mfi.size_tcpinfo_user = sizeof(struct tcp_info);
mfi.size_sfinfo_user = sizeof(struct mptcp_subflow_info);
mfi.size_arrays_user = 2;
mfi.subflow_info = (unsigned long)&sfinfo[0];
mfi.tcp_info = (unsigned long)&tcp_info[0];
olen = data_size;
ret = getsockopt(fd, SOL_MPTCP, MPTCP_FULL_INFO, &mfi, &olen);
if (ret < 0) {
if (errno == EOPNOTSUPP) {
perror("MPTCP_FULL_INFO test skipped");
return;
}
xerror("getsockopt MPTCP_FULL_INFO");
}
assert(olen <= data_size);
assert(mfi.size_tcpinfo_kernel > 0);
assert(mfi.size_tcpinfo_user ==
MIN(mfi.size_tcpinfo_kernel, sizeof(struct tcp_info)));
assert(mfi.size_sfinfo_kernel > 0);
assert(mfi.size_sfinfo_user ==
MIN(mfi.size_sfinfo_kernel, sizeof(struct mptcp_subflow_info)));
assert(mfi.num_subflows == 1);
/* Tolerate future extension to mptcp_info struct and running newer
* test on top of older kernel.
* Anyway any kernel supporting MPTCP_FULL_INFO must at least include
* the following in mptcp_info.
*/
assert(olen > (socklen_t)__builtin_offsetof(struct mptcp_full_info, tcp_info));
assert(mfi.mptcp_info.mptcpi_subflows == 0);
assert(mfi.mptcp_info.mptcpi_bytes_sent == s->last_sample.mptcpi_bytes_sent);
assert(mfi.mptcp_info.mptcpi_bytes_received == s->last_sample.mptcpi_bytes_received);
assert(sfinfo[0].id == 1);
assert(tcp_info[0].tcpi_bytes_sent == s->tcp_info.tcpi_bytes_sent);
assert(tcp_info[0].tcpi_bytes_received == s->tcp_info.tcpi_bytes_received);
assert(!memcmp(&sfinfo->addrs, &s->addrs, sizeof(struct mptcp_subflow_addrs)));
}
static void do_getsockopts(struct so_state *s, int fd, size_t r, size_t w)
{
do_getsockopt_mptcp_info(s, fd, w);
do_getsockopt_tcp_info(s, fd, r, w);
do_getsockopt_subflow_addrs(fd);
do_getsockopt_subflow_addrs(s, fd);
if (r)
do_getsockopt_mptcp_full_info(s, fd);
}
static void connect_one_server(int fd, int pipefd)