Message ID | 3b120e58e14cc6884bc53545ea1c80bca3fb43f7.1740035000.git.yangang@kylinos.cn (mailing list archive) |
---|---|
State | Superseded, archived |
Delegated to: | Matthieu Baerts |
Headers | show |
Series | selftests: mptcp: add tests for increasing | expand |
Context | Check | Description |
---|---|---|
matttbe/build | success | Build and static analysis OK |
matttbe/checkpatch | warning | total: 0 errors, 2 warnings, 5 checks, 243 lines checked |
matttbe/shellcheck | success | MPTCP selftests files have not been modified |
matttbe/KVM_Validation__normal | success | Success! ✅ |
matttbe/KVM_Validation__debug | success | Success! ✅ |
matttbe/KVM_Validation__btf-normal__only_bpftest_all_ | success | Success! ✅ |
matttbe/KVM_Validation__btf-debug__only_bpftest_all_ | success | Success! ✅ |
Hi Gang Yan, On 20/02/2025 08:11, Gang Yan wrote: > This patch enables the retrieval of the mptcp_info structure corresponding > to a specified MPTCP socket (msk). When multiple MPTCP connections are > present, specific information can be obtained for a given connection > through the 'mptcp_diag_dump_one' by using the 'token' associated with > the msk. Thank you for the v2. I had a very quick look, mainly because I saw CheckPatch and Shellcheck were complaining about this series. Do you mind looking at that please? https://github.com/multipath-tcp/mptcp_net-next/actions/runs/13430040104 > > Signed-off-by: Gang Yan <yangang@kylinos.cn> > --- > tools/testing/selftests/net/mptcp/Makefile | 2 +- > .../testing/selftests/net/mptcp/mptcp_diag.c | 235 ++++++++++++++++++ > 2 files changed, 236 insertions(+), 1 deletion(-) > create mode 100644 tools/testing/selftests/net/mptcp/mptcp_diag.c > > diff --git a/tools/testing/selftests/net/mptcp/Makefile b/tools/testing/selftests/net/mptcp/Makefile > index c76525fe2b84..340e1a777e16 100644 > --- a/tools/testing/selftests/net/mptcp/Makefile > +++ b/tools/testing/selftests/net/mptcp/Makefile > @@ -7,7 +7,7 @@ CFLAGS += -Wall -Wl,--no-as-needed -O2 -g -I$(top_srcdir)/usr/include $(KHDR_INC > TEST_PROGS := mptcp_connect.sh pm_netlink.sh mptcp_join.sh diag.sh \ > simult_flows.sh mptcp_sockopt.sh userspace_pm.sh > > -TEST_GEN_FILES = mptcp_connect pm_nl_ctl mptcp_sockopt mptcp_inq > +TEST_GEN_FILES = mptcp_connect pm_nl_ctl mptcp_sockopt mptcp_inq mptcp_diag > > TEST_FILES := mptcp_lib.sh settings > > diff --git a/tools/testing/selftests/net/mptcp/mptcp_diag.c b/tools/testing/selftests/net/mptcp/mptcp_diag.c > new file mode 100644 > index 000000000000..3e0be9e58912 > --- /dev/null > +++ b/tools/testing/selftests/net/mptcp/mptcp_diag.c > @@ -0,0 +1,235 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* Copyright (c) 2025, Kylin Software*/ > + > +#include <linux/sock_diag.h> > +#include <linux/rtnetlink.h> > +#include <linux/inet_diag.h> > +#include <linux/netlink.h> > +#include <sys/socket.h> > +#include <netinet/in.h> > +#include <linux/tcp.h> > + > +#include <unistd.h> > +#include <stdlib.h> > +#include <string.h> > +#include <errno.h> > +#include <stdio.h> > + > +#ifndef IPPROTO_MPTCP > +#define IPPROTO_MPTCP 262 > +#endif > + > +#ifndef MPTCP_INFO > +struct mptcp_info { > + __u8 mptcpi_subflows; > + __u8 mptcpi_add_addr_signal; > + __u8 mptcpi_add_addr_accepted; > + __u8 mptcpi_subflows_max; > + __u8 mptcpi_add_addr_signal_max; > + __u8 mptcpi_add_addr_accepted_max; > + __u32 mptcpi_flags; > + __u32 mptcpi_token; > + __u64 mptcpi_write_seq; > + __u64 mptcpi_snd_una; > + __u64 mptcpi_rcv_nxt; > + __u8 mptcpi_local_addr_used; > + __u8 mptcpi_local_addr_max; > + __u8 mptcpi_csum_enabled; > + __u32 mptcpi_retransmits; > + __u64 mptcpi_bytes_retrans; > + __u64 mptcpi_bytes_sent; > + __u64 mptcpi_bytes_received; > + __u64 mptcpi_bytes_acked; > +}; > + > +#define MPTCP_INFO 1 > +#endif > + > +static void die_perror(const char *msg) > +{ > + perror(msg); > + exit(1); > +} > + > +static void die_usage(int r) > +{ > + fprintf(stderr, "Usage: mptcp_diag -t\n"); > + exit(r); > +} > + > +static void send_query(int fd, __u32 token) > +{ > + struct sockaddr_nl nladdr = { > + .nl_family = AF_NETLINK > + }; > + struct { > + struct nlmsghdr nlh; > + struct inet_diag_req_v2 r; > + } req = { > + .nlh = { > + .nlmsg_len = sizeof(req), > + .nlmsg_type = SOCK_DIAG_BY_FAMILY, > + .nlmsg_flags = NLM_F_REQUEST > + }, > + .r = { > + .sdiag_family = AF_INET, > + .sdiag_protocol = IPPROTO_MPTCP, > + .id.idiag_cookie[0] = token, > + } > + }; > + struct rtattr rta_proto; > + struct iovec iov[6]; > + int iovlen = 1; > + __u32 proto; > + > + req.r.idiag_ext |= (1 << (INET_DIAG_INFO - 1)); > + proto = IPPROTO_MPTCP; > + rta_proto.rta_type = INET_DIAG_REQ_PROTOCOL; > + rta_proto.rta_len = RTA_LENGTH(sizeof(proto)); > + > + iov[0] = (struct iovec) { > + .iov_base = &req, > + .iov_len = sizeof(req) > + }; > + iov[iovlen] = (struct iovec){ &rta_proto, sizeof(rta_proto)}; > + iov[iovlen + 1] = (struct iovec){ &proto, sizeof(proto)}; > + req.nlh.nlmsg_len += RTA_LENGTH(sizeof(proto)); > + iovlen += 2; > + struct msghdr msg = { > + .msg_name = &nladdr, > + .msg_namelen = sizeof(nladdr), > + .msg_iov = iov, > + .msg_iovlen = iovlen > + }; > + > + for (;;) { > + if (sendmsg(fd, &msg, 0) < 0) { > + if (errno == EINTR) > + continue; > + die_perror("sendmsg"); > + } > + break; > + } > +} > + > +static void parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta, > + int len, unsigned short flags) (the indentation doesn't look OK) > +{ > + unsigned short type; > + > + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); > + while (RTA_OK(rta, len)) { > + type = rta->rta_type & ~flags; > + if ((type <= max) && (!tb[type])) > + tb[type] = rta; > + rta = RTA_NEXT(rta, len); > + } > +} > + > +static struct mptcp_info *parse_nlmsg(struct nlmsghdr *nlh) > +{ > + struct inet_diag_msg *r = NLMSG_DATA(nlh); > + struct rtattr *tb[INET_DIAG_MAX+1]; > + struct mptcp_info *info; > + > + parse_rtattr_flags(tb, INET_DIAG_MAX, (struct rtattr *)(r+1), > + nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r)), > + NLA_F_NESTED); > + > + if (tb[INET_DIAG_INFO]) { > + info = RTA_DATA(tb[INET_DIAG_INFO]); > + return info; The cope of "info" is local if it is a pointer from 'tb' that as been allocated on the stack. A pointer to this local structure should then not be returned. Either allocate memory on heap, or probably better: a pointer to an mptcp_info structure should be passed to get_mptcpinfo() -> recv_nlmsg() -> parse_nlmsg(). Or use a global variable. So here, we would fill this space, and read it later on. (Or, if you only need the token, only return that, but that's strange to give the token, and only get the token in return...) (Or: don't return anything here, but simply print each field of the mptcp_info structure → but that sounds better to do that after having called get_mptcpinfo().) > + } > + > + die_perror("Has no INET_DIAG_INFO"); > + return info; "info" might not have been assigned. > +} > + > +static struct mptcp_info *recv_nlmsg(int fd, struct nlmsghdr *nlh) > +{ > + char rcv_buff[8192]; > + struct sockaddr_nl rcv_nladdr = { > + .nl_family = AF_NETLINK > + }; > + struct iovec rcv_iov = { > + .iov_base = rcv_buff, > + .iov_len = sizeof(rcv_buff) > + }; > + struct msghdr rcv_msg = { > + .msg_name = &rcv_nladdr, > + .msg_namelen = sizeof(rcv_nladdr), > + .msg_iov = &rcv_iov, > + .msg_iovlen = 1 > + }; > + struct mptcp_info *info = NULL; > + int len; > + > + len = recvmsg(fd, &rcv_msg, 0); > + nlh = (struct nlmsghdr *)rcv_buff; > + > + while (NLMSG_OK(nlh, len)) { > + if (nlh->nlmsg_type == NLMSG_DONE) { > + printf("NLMSG_DONE\n"); > + break; > + } else if (nlh->nlmsg_type == NLMSG_ERROR) { > + struct nlmsgerr *err; > + > + err = (struct nlmsgerr *)NLMSG_DATA(nlh); > + printf("Error %d:%s\n", -(err->error), strerror(-(err->error))); > + break; > + } > + info = parse_nlmsg(nlh); > + nlh = NLMSG_NEXT(nlh, len); > + } > + close(fd); That's strange to close the fd here. Probably best to do that in get_mptcpinfo() where it has been opened. > + return info; > +} > + > +static struct mptcp_info *get_mptcpinfo(__u32 token) > +{ > + struct nlmsghdr *nlh = NULL; > + int fd; > + > + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG); > + if (fd < 0) > + die_perror("Netlink socket"); > + > + send_query(fd, token); > + return recv_nlmsg(fd, nlh); > +} > + > +static void parse_opts(int argc, char **argv, __u32 *target_token) > +{ > + int c; > + > + while ((c = getopt(argc, argv, "ht:")) != -1) { > + switch (c) { > + case 'h': > + die_usage(1); exit(0) for -h. > + break; > + case 't': > + sscanf(optarg, "%x", target_token); > + break; > + default: > + die_usage(1); > + break; > + } > + } It might be good to call "die_usage(1)" if the program has not been called with '-t'. > +} > + > +int main(int argc, char *argv[]) > +{ > + struct mptcp_info *info = NULL; > + __u32 target_token; > + > + parse_opts(argc, argv, &target_token); > + info = get_mptcpinfo(target_token); > + > + if (info) > + printf("token:%x\n", info->mptcpi_token); > + else > + perror("No Such msk using this token!\n"); > + > + return 0; > +} > + Cheers, Matt
diff --git a/tools/testing/selftests/net/mptcp/Makefile b/tools/testing/selftests/net/mptcp/Makefile index c76525fe2b84..340e1a777e16 100644 --- a/tools/testing/selftests/net/mptcp/Makefile +++ b/tools/testing/selftests/net/mptcp/Makefile @@ -7,7 +7,7 @@ CFLAGS += -Wall -Wl,--no-as-needed -O2 -g -I$(top_srcdir)/usr/include $(KHDR_INC TEST_PROGS := mptcp_connect.sh pm_netlink.sh mptcp_join.sh diag.sh \ simult_flows.sh mptcp_sockopt.sh userspace_pm.sh -TEST_GEN_FILES = mptcp_connect pm_nl_ctl mptcp_sockopt mptcp_inq +TEST_GEN_FILES = mptcp_connect pm_nl_ctl mptcp_sockopt mptcp_inq mptcp_diag TEST_FILES := mptcp_lib.sh settings diff --git a/tools/testing/selftests/net/mptcp/mptcp_diag.c b/tools/testing/selftests/net/mptcp/mptcp_diag.c new file mode 100644 index 000000000000..3e0be9e58912 --- /dev/null +++ b/tools/testing/selftests/net/mptcp/mptcp_diag.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025, Kylin Software*/ + +#include <linux/sock_diag.h> +#include <linux/rtnetlink.h> +#include <linux/inet_diag.h> +#include <linux/netlink.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <linux/tcp.h> + +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> + +#ifndef IPPROTO_MPTCP +#define IPPROTO_MPTCP 262 +#endif + +#ifndef MPTCP_INFO +struct mptcp_info { + __u8 mptcpi_subflows; + __u8 mptcpi_add_addr_signal; + __u8 mptcpi_add_addr_accepted; + __u8 mptcpi_subflows_max; + __u8 mptcpi_add_addr_signal_max; + __u8 mptcpi_add_addr_accepted_max; + __u32 mptcpi_flags; + __u32 mptcpi_token; + __u64 mptcpi_write_seq; + __u64 mptcpi_snd_una; + __u64 mptcpi_rcv_nxt; + __u8 mptcpi_local_addr_used; + __u8 mptcpi_local_addr_max; + __u8 mptcpi_csum_enabled; + __u32 mptcpi_retransmits; + __u64 mptcpi_bytes_retrans; + __u64 mptcpi_bytes_sent; + __u64 mptcpi_bytes_received; + __u64 mptcpi_bytes_acked; +}; + +#define MPTCP_INFO 1 +#endif + +static void die_perror(const char *msg) +{ + perror(msg); + exit(1); +} + +static void die_usage(int r) +{ + fprintf(stderr, "Usage: mptcp_diag -t\n"); + exit(r); +} + +static void send_query(int fd, __u32 token) +{ + struct sockaddr_nl nladdr = { + .nl_family = AF_NETLINK + }; + struct { + struct nlmsghdr nlh; + struct inet_diag_req_v2 r; + } req = { + .nlh = { + .nlmsg_len = sizeof(req), + .nlmsg_type = SOCK_DIAG_BY_FAMILY, + .nlmsg_flags = NLM_F_REQUEST + }, + .r = { + .sdiag_family = AF_INET, + .sdiag_protocol = IPPROTO_MPTCP, + .id.idiag_cookie[0] = token, + } + }; + struct rtattr rta_proto; + struct iovec iov[6]; + int iovlen = 1; + __u32 proto; + + req.r.idiag_ext |= (1 << (INET_DIAG_INFO - 1)); + proto = IPPROTO_MPTCP; + rta_proto.rta_type = INET_DIAG_REQ_PROTOCOL; + rta_proto.rta_len = RTA_LENGTH(sizeof(proto)); + + iov[0] = (struct iovec) { + .iov_base = &req, + .iov_len = sizeof(req) + }; + iov[iovlen] = (struct iovec){ &rta_proto, sizeof(rta_proto)}; + iov[iovlen + 1] = (struct iovec){ &proto, sizeof(proto)}; + req.nlh.nlmsg_len += RTA_LENGTH(sizeof(proto)); + iovlen += 2; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = iov, + .msg_iovlen = iovlen + }; + + for (;;) { + if (sendmsg(fd, &msg, 0) < 0) { + if (errno == EINTR) + continue; + die_perror("sendmsg"); + } + break; + } +} + +static void parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta, + int len, unsigned short flags) +{ + unsigned short type; + + memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); + while (RTA_OK(rta, len)) { + type = rta->rta_type & ~flags; + if ((type <= max) && (!tb[type])) + tb[type] = rta; + rta = RTA_NEXT(rta, len); + } +} + +static struct mptcp_info *parse_nlmsg(struct nlmsghdr *nlh) +{ + struct inet_diag_msg *r = NLMSG_DATA(nlh); + struct rtattr *tb[INET_DIAG_MAX+1]; + struct mptcp_info *info; + + parse_rtattr_flags(tb, INET_DIAG_MAX, (struct rtattr *)(r+1), + nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r)), + NLA_F_NESTED); + + if (tb[INET_DIAG_INFO]) { + info = RTA_DATA(tb[INET_DIAG_INFO]); + return info; + } + + die_perror("Has no INET_DIAG_INFO"); + return info; +} + +static struct mptcp_info *recv_nlmsg(int fd, struct nlmsghdr *nlh) +{ + char rcv_buff[8192]; + struct sockaddr_nl rcv_nladdr = { + .nl_family = AF_NETLINK + }; + struct iovec rcv_iov = { + .iov_base = rcv_buff, + .iov_len = sizeof(rcv_buff) + }; + struct msghdr rcv_msg = { + .msg_name = &rcv_nladdr, + .msg_namelen = sizeof(rcv_nladdr), + .msg_iov = &rcv_iov, + .msg_iovlen = 1 + }; + struct mptcp_info *info = NULL; + int len; + + len = recvmsg(fd, &rcv_msg, 0); + nlh = (struct nlmsghdr *)rcv_buff; + + while (NLMSG_OK(nlh, len)) { + if (nlh->nlmsg_type == NLMSG_DONE) { + printf("NLMSG_DONE\n"); + break; + } else if (nlh->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err; + + err = (struct nlmsgerr *)NLMSG_DATA(nlh); + printf("Error %d:%s\n", -(err->error), strerror(-(err->error))); + break; + } + info = parse_nlmsg(nlh); + nlh = NLMSG_NEXT(nlh, len); + } + close(fd); + return info; +} + +static struct mptcp_info *get_mptcpinfo(__u32 token) +{ + struct nlmsghdr *nlh = NULL; + int fd; + + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG); + if (fd < 0) + die_perror("Netlink socket"); + + send_query(fd, token); + return recv_nlmsg(fd, nlh); +} + +static void parse_opts(int argc, char **argv, __u32 *target_token) +{ + int c; + + while ((c = getopt(argc, argv, "ht:")) != -1) { + switch (c) { + case 'h': + die_usage(1); + break; + case 't': + sscanf(optarg, "%x", target_token); + break; + default: + die_usage(1); + break; + } + } +} + +int main(int argc, char *argv[]) +{ + struct mptcp_info *info = NULL; + __u32 target_token; + + parse_opts(argc, argv, &target_token); + info = get_mptcpinfo(target_token); + + if (info) + printf("token:%x\n", info->mptcpi_token); + else + perror("No Such msk using this token!\n"); + + return 0; +} +
This patch enables the retrieval of the mptcp_info structure corresponding to a specified MPTCP socket (msk). When multiple MPTCP connections are present, specific information can be obtained for a given connection through the 'mptcp_diag_dump_one' by using the 'token' associated with the msk. Signed-off-by: Gang Yan <yangang@kylinos.cn> --- tools/testing/selftests/net/mptcp/Makefile | 2 +- .../testing/selftests/net/mptcp/mptcp_diag.c | 235 ++++++++++++++++++ 2 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/net/mptcp/mptcp_diag.c