Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests: mptcp: add helpers to get subflow_info

This patch adds 'get_subflow_info' in 'mptcp_diag', which can check whether
a TCP connection is an MPTCP subflow based on the "INET_ULP_INFO_MPTCP"
with tcp_diag method.

The helper 'print_subflow_info' in 'mptcp_diag' can print the subflow_filed
of an MPTCP subflow for further checking the 'subflow_info' through
inet_diag method.

The example of the whole output should be:

$ ./mptcp_diag -s "127.0.0.1:10000 127.0.0.1:38984"
127.0.0.1:10000 -> 127.0.0.1:38984
It's a mptcp subflow, the subflow info:
flags:Mec token:0000(id:0)/4278e77e(id:0) seq:9288466187236176036 \
sfseq:1 ssnoff:2317083055 maplen:215

Co-developed-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Gang Yan <yangang@kylinos.cn>
Reviewed-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
Signed-off-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
Link: https://patch.msgid.link/20250502-net-next-mptcp-sft-inc-cover-v1-6-68eec95898fb@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Gang Yan and committed by
Jakub Kicinski
c7ac7452 caa6811c

+157 -2
+157 -2
tools/testing/selftests/net/mptcp/mptcp_diag.c
··· 8 8 #include <sys/socket.h> 9 9 #include <netinet/in.h> 10 10 #include <linux/tcp.h> 11 + #include <arpa/inet.h> 11 12 12 13 #include <unistd.h> 13 14 #include <stdlib.h> ··· 20 19 #define IPPROTO_MPTCP 262 21 20 #endif 22 21 22 + #define parse_rtattr_nested(tb, max, rta) \ 23 + (parse_rtattr_flags((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta), \ 24 + NLA_F_NESTED)) 25 + 23 26 struct params { 24 27 __u32 target_token; 28 + char subflow_addrs[1024]; 25 29 }; 26 30 27 31 struct mptcp_info { ··· 56 50 __u32 mptcpi_last_ack_recv; 57 51 }; 58 52 53 + enum { 54 + MPTCP_SUBFLOW_ATTR_UNSPEC, 55 + MPTCP_SUBFLOW_ATTR_TOKEN_REM, 56 + MPTCP_SUBFLOW_ATTR_TOKEN_LOC, 57 + MPTCP_SUBFLOW_ATTR_RELWRITE_SEQ, 58 + MPTCP_SUBFLOW_ATTR_MAP_SEQ, 59 + MPTCP_SUBFLOW_ATTR_MAP_SFSEQ, 60 + MPTCP_SUBFLOW_ATTR_SSN_OFFSET, 61 + MPTCP_SUBFLOW_ATTR_MAP_DATALEN, 62 + MPTCP_SUBFLOW_ATTR_FLAGS, 63 + MPTCP_SUBFLOW_ATTR_ID_REM, 64 + MPTCP_SUBFLOW_ATTR_ID_LOC, 65 + MPTCP_SUBFLOW_ATTR_PAD, 66 + 67 + __MPTCP_SUBFLOW_ATTR_MAX 68 + }; 69 + 70 + #define MPTCP_SUBFLOW_ATTR_MAX (__MPTCP_SUBFLOW_ATTR_MAX - 1) 71 + 72 + #define MPTCP_SUBFLOW_FLAG_MCAP_REM _BITUL(0) 73 + #define MPTCP_SUBFLOW_FLAG_MCAP_LOC _BITUL(1) 74 + #define MPTCP_SUBFLOW_FLAG_JOIN_REM _BITUL(2) 75 + #define MPTCP_SUBFLOW_FLAG_JOIN_LOC _BITUL(3) 76 + #define MPTCP_SUBFLOW_FLAG_BKUP_REM _BITUL(4) 77 + #define MPTCP_SUBFLOW_FLAG_BKUP_LOC _BITUL(5) 78 + #define MPTCP_SUBFLOW_FLAG_FULLY_ESTABLISHED _BITUL(6) 79 + #define MPTCP_SUBFLOW_FLAG_CONNECTED _BITUL(7) 80 + #define MPTCP_SUBFLOW_FLAG_MAPVALID _BITUL(8) 81 + 82 + #define rta_getattr(type, value) (*(type *)RTA_DATA(value)) 83 + 59 84 static void die_perror(const char *msg) 60 85 { 61 86 perror(msg); ··· 95 58 96 59 static void die_usage(int r) 97 60 { 98 - fprintf(stderr, "Usage: mptcp_diag -t\n"); 61 + fprintf(stderr, "Usage:\n" 62 + "mptcp_diag -t <token>\n" 63 + "mptcp_diag -s \"<saddr>:<sport> <daddr>:<dport>\"\n"); 99 64 exit(r); 100 65 } 101 66 ··· 198 159 printf("bytes_acked: %llu\n", info->mptcpi_bytes_acked); 199 160 } 200 161 162 + /* 163 + * 'print_subflow_info' is from 'mptcp_subflow_info' 164 + * which is a function in 'misc/ss.c' of iproute2. 165 + */ 166 + static void print_subflow_info(struct rtattr *tb[]) 167 + { 168 + u_int32_t flags = 0; 169 + 170 + printf("It's a mptcp subflow, the subflow info:\n"); 171 + if (tb[MPTCP_SUBFLOW_ATTR_FLAGS]) { 172 + char caps[32 + 1] = { 0 }, *cap = &caps[0]; 173 + 174 + flags = rta_getattr(__u32, tb[MPTCP_SUBFLOW_ATTR_FLAGS]); 175 + 176 + if (flags & MPTCP_SUBFLOW_FLAG_MCAP_REM) 177 + *cap++ = 'M'; 178 + if (flags & MPTCP_SUBFLOW_FLAG_MCAP_LOC) 179 + *cap++ = 'm'; 180 + if (flags & MPTCP_SUBFLOW_FLAG_JOIN_REM) 181 + *cap++ = 'J'; 182 + if (flags & MPTCP_SUBFLOW_FLAG_JOIN_LOC) 183 + *cap++ = 'j'; 184 + if (flags & MPTCP_SUBFLOW_FLAG_BKUP_REM) 185 + *cap++ = 'B'; 186 + if (flags & MPTCP_SUBFLOW_FLAG_BKUP_LOC) 187 + *cap++ = 'b'; 188 + if (flags & MPTCP_SUBFLOW_FLAG_FULLY_ESTABLISHED) 189 + *cap++ = 'e'; 190 + if (flags & MPTCP_SUBFLOW_FLAG_CONNECTED) 191 + *cap++ = 'c'; 192 + if (flags & MPTCP_SUBFLOW_FLAG_MAPVALID) 193 + *cap++ = 'v'; 194 + 195 + if (flags) 196 + printf(" flags:%s", caps); 197 + } 198 + if (tb[MPTCP_SUBFLOW_ATTR_TOKEN_REM] && 199 + tb[MPTCP_SUBFLOW_ATTR_TOKEN_LOC] && 200 + tb[MPTCP_SUBFLOW_ATTR_ID_REM] && 201 + tb[MPTCP_SUBFLOW_ATTR_ID_LOC]) 202 + printf(" token:%04x(id:%u)/%04x(id:%u)", 203 + rta_getattr(__u32, tb[MPTCP_SUBFLOW_ATTR_TOKEN_REM]), 204 + rta_getattr(__u8, tb[MPTCP_SUBFLOW_ATTR_ID_REM]), 205 + rta_getattr(__u32, tb[MPTCP_SUBFLOW_ATTR_TOKEN_LOC]), 206 + rta_getattr(__u8, tb[MPTCP_SUBFLOW_ATTR_ID_LOC])); 207 + if (tb[MPTCP_SUBFLOW_ATTR_MAP_SEQ]) 208 + printf(" seq:%llu", 209 + rta_getattr(__u64, tb[MPTCP_SUBFLOW_ATTR_MAP_SEQ])); 210 + if (tb[MPTCP_SUBFLOW_ATTR_MAP_SFSEQ]) 211 + printf(" sfseq:%u", 212 + rta_getattr(__u32, tb[MPTCP_SUBFLOW_ATTR_MAP_SFSEQ])); 213 + if (tb[MPTCP_SUBFLOW_ATTR_SSN_OFFSET]) 214 + printf(" ssnoff:%u", 215 + rta_getattr(__u32, tb[MPTCP_SUBFLOW_ATTR_SSN_OFFSET])); 216 + if (tb[MPTCP_SUBFLOW_ATTR_MAP_DATALEN]) 217 + printf(" maplen:%u", 218 + rta_getattr(__u32, tb[MPTCP_SUBFLOW_ATTR_MAP_DATALEN])); 219 + printf("\n"); 220 + } 221 + 201 222 static void parse_nlmsg(struct nlmsghdr *nlh, __u32 proto) 202 223 { 203 224 struct inet_diag_msg *r = NLMSG_DATA(nlh); ··· 280 181 info = RTA_DATA(tb[INET_DIAG_INFO]); 281 182 } 282 183 print_info_msg(info); 184 + } 185 + if (proto == IPPROTO_TCP && tb[INET_DIAG_ULP_INFO]) { 186 + struct rtattr *ulpinfo[INET_ULP_INFO_MAX + 1] = { 0 }; 187 + 188 + parse_rtattr_nested(ulpinfo, INET_ULP_INFO_MAX, 189 + tb[INET_DIAG_ULP_INFO]); 190 + 191 + if (ulpinfo[INET_ULP_INFO_MPTCP]) { 192 + struct rtattr *sfinfo[MPTCP_SUBFLOW_ATTR_MAX + 1] = { 0 }; 193 + 194 + parse_rtattr_nested(sfinfo, MPTCP_SUBFLOW_ATTR_MAX, 195 + ulpinfo[INET_ULP_INFO_MPTCP]); 196 + print_subflow_info(sfinfo); 197 + } else { 198 + printf("It's a normal TCP!\n"); 199 + } 283 200 } 284 201 } 285 202 ··· 359 244 close(fd); 360 245 } 361 246 247 + static void get_subflow_info(char *subflow_addrs) 248 + { 249 + struct inet_diag_req_v2 r = { 250 + .sdiag_family = AF_INET, 251 + .sdiag_protocol = IPPROTO_TCP, 252 + .idiag_ext = 1 << (INET_DIAG_INFO - 1), 253 + .id.idiag_cookie[0] = INET_DIAG_NOCOOKIE, 254 + .id.idiag_cookie[1] = INET_DIAG_NOCOOKIE, 255 + }; 256 + char saddr[64], daddr[64]; 257 + int sport, dport; 258 + int ret; 259 + int fd; 260 + 261 + ret = sscanf(subflow_addrs, "%[^:]:%d %[^:]:%d", saddr, &sport, daddr, &dport); 262 + if (ret != 4) 263 + die_perror("IP PORT Pairs has style problems!"); 264 + 265 + printf("%s:%d -> %s:%d\n", saddr, sport, daddr, dport); 266 + 267 + fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG); 268 + if (fd < 0) 269 + die_perror("Netlink socket"); 270 + 271 + r.id.idiag_sport = htons(sport); 272 + r.id.idiag_dport = htons(dport); 273 + 274 + inet_pton(AF_INET, saddr, &r.id.idiag_src); 275 + inet_pton(AF_INET, daddr, &r.id.idiag_dst); 276 + send_query(fd, &r, IPPROTO_TCP); 277 + recv_nlmsg(fd, IPPROTO_TCP); 278 + } 279 + 362 280 static void parse_opts(int argc, char **argv, struct params *p) 363 281 { 364 282 int c; ··· 399 251 if (argc < 2) 400 252 die_usage(1); 401 253 402 - while ((c = getopt(argc, argv, "ht:")) != -1) { 254 + while ((c = getopt(argc, argv, "ht:s:")) != -1) { 403 255 switch (c) { 404 256 case 'h': 405 257 die_usage(0); 406 258 break; 407 259 case 't': 408 260 sscanf(optarg, "%x", &p->target_token); 261 + break; 262 + case 's': 263 + strncpy(p->subflow_addrs, optarg, 264 + sizeof(p->subflow_addrs) - 1); 409 265 break; 410 266 default: 411 267 die_usage(1); ··· 426 274 427 275 if (p.target_token) 428 276 get_mptcpinfo(p.target_token); 277 + 278 + if (p.subflow_addrs[0] != '\0') 279 + get_subflow_info(p.subflow_addrs); 429 280 430 281 return 0; 431 282 }