Commit aa723d5b authored by Paolo Abeni's avatar Paolo Abeni Committed by Jakub Kicinski
Browse files

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: default avatarPaolo Abeni <pabeni@redhat.com>
Reviewed-by: default avatarMatthieu Baerts <matthieu.baerts@tessares.net>
Co-developed-by: default avatarMatthieu Baerts <matthieu.baerts@tessares.net>
Signed-off-by: default avatarMatthieu Baerts <matthieu.baerts@tessares.net>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 49243207
Loading
Loading
Loading
Loading
+91 −2
Original line number Diff line number Diff line
@@ -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 @@ static void do_getsockopt_tcp_info(struct so_state *s, int fd, size_t r, size_t
	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)