selftests: mptcp: add MPTCP_FULL_INFO testcase
authorPaolo Abeni <pabeni@redhat.com>
Tue, 20 Jun 2023 16:30:19 +0000 (18:30 +0200)
committerJakub Kicinski <kuba@kernel.org>
Thu, 22 Jun 2023 05:45:58 +0000 (22:45 -0700)
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>
tools/testing/selftests/net/mptcp/mptcp_sockopt.c

index 5ee710b..926b0be 100644 (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)