1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2025, Kylin Software */ 3 4 #include <errno.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <unistd.h> 9 10 #include <sys/socket.h> 11 12 #include <arpa/inet.h> 13 14 #include <netinet/in.h> 15 16 #include <linux/compiler.h> 17 #include <linux/inet_diag.h> 18 #include <linux/netlink.h> 19 #include <linux/rtnetlink.h> 20 #include <linux/sock_diag.h> 21 #include <linux/tcp.h> 22 23 #ifndef IPPROTO_MPTCP 24 #define IPPROTO_MPTCP 262 25 #endif 26 27 #define parse_rtattr_nested(tb, max, rta) \ 28 (parse_rtattr_flags((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta), \ 29 NLA_F_NESTED)) 30 31 struct params { 32 __u32 target_token; 33 char subflow_addrs[1024]; 34 }; 35 36 struct mptcp_info { 37 __u8 mptcpi_subflows; 38 __u8 mptcpi_add_addr_signal; 39 __u8 mptcpi_add_addr_accepted; 40 __u8 mptcpi_subflows_max; 41 __u8 mptcpi_add_addr_signal_max; 42 __u8 mptcpi_add_addr_accepted_max; 43 __u32 mptcpi_flags; 44 __u32 mptcpi_token; 45 __u64 mptcpi_write_seq; 46 __u64 mptcpi_snd_una; 47 __u64 mptcpi_rcv_nxt; 48 __u8 mptcpi_local_addr_used; 49 __u8 mptcpi_local_addr_max; 50 __u8 mptcpi_csum_enabled; 51 __u32 mptcpi_retransmits; 52 __u64 mptcpi_bytes_retrans; 53 __u64 mptcpi_bytes_sent; 54 __u64 mptcpi_bytes_received; 55 __u64 mptcpi_bytes_acked; 56 __u8 mptcpi_subflows_total; 57 __u8 reserved[3]; 58 __u32 mptcpi_last_data_sent; 59 __u32 mptcpi_last_data_recv; 60 __u32 mptcpi_last_ack_recv; 61 }; 62 63 enum { 64 MPTCP_SUBFLOW_ATTR_UNSPEC, 65 MPTCP_SUBFLOW_ATTR_TOKEN_REM, 66 MPTCP_SUBFLOW_ATTR_TOKEN_LOC, 67 MPTCP_SUBFLOW_ATTR_RELWRITE_SEQ, 68 MPTCP_SUBFLOW_ATTR_MAP_SEQ, 69 MPTCP_SUBFLOW_ATTR_MAP_SFSEQ, 70 MPTCP_SUBFLOW_ATTR_SSN_OFFSET, 71 MPTCP_SUBFLOW_ATTR_MAP_DATALEN, 72 MPTCP_SUBFLOW_ATTR_FLAGS, 73 MPTCP_SUBFLOW_ATTR_ID_REM, 74 MPTCP_SUBFLOW_ATTR_ID_LOC, 75 MPTCP_SUBFLOW_ATTR_PAD, 76 77 __MPTCP_SUBFLOW_ATTR_MAX 78 }; 79 80 #define MPTCP_SUBFLOW_ATTR_MAX (__MPTCP_SUBFLOW_ATTR_MAX - 1) 81 82 #define MPTCP_SUBFLOW_FLAG_MCAP_REM _BITUL(0) 83 #define MPTCP_SUBFLOW_FLAG_MCAP_LOC _BITUL(1) 84 #define MPTCP_SUBFLOW_FLAG_JOIN_REM _BITUL(2) 85 #define MPTCP_SUBFLOW_FLAG_JOIN_LOC _BITUL(3) 86 #define MPTCP_SUBFLOW_FLAG_BKUP_REM _BITUL(4) 87 #define MPTCP_SUBFLOW_FLAG_BKUP_LOC _BITUL(5) 88 #define MPTCP_SUBFLOW_FLAG_FULLY_ESTABLISHED _BITUL(6) 89 #define MPTCP_SUBFLOW_FLAG_CONNECTED _BITUL(7) 90 #define MPTCP_SUBFLOW_FLAG_MAPVALID _BITUL(8) 91 92 #define rta_getattr(type, value) (*(type *)RTA_DATA(value)) 93 94 static void __noreturn die_perror(const char *msg) 95 { 96 perror(msg); 97 exit(1); 98 } 99 100 static void die_usage(int r) 101 { 102 fprintf(stderr, "Usage:\n" 103 "mptcp_diag -t <token>\n" 104 "mptcp_diag -s \"<saddr>:<sport> <daddr>:<dport>\"\n"); 105 exit(r); 106 } 107 108 static void send_query(int fd, struct inet_diag_req_v2 *r, __u32 proto) 109 { 110 struct sockaddr_nl nladdr = { 111 .nl_family = AF_NETLINK 112 }; 113 struct { 114 struct nlmsghdr nlh; 115 struct inet_diag_req_v2 r; 116 } req = { 117 .nlh = { 118 .nlmsg_len = sizeof(req), 119 .nlmsg_type = SOCK_DIAG_BY_FAMILY, 120 .nlmsg_flags = NLM_F_REQUEST 121 }, 122 .r = *r 123 }; 124 struct rtattr rta_proto; 125 struct iovec iov[6]; 126 int iovlen = 0; 127 128 iov[iovlen++] = (struct iovec) { 129 .iov_base = &req, 130 .iov_len = sizeof(req) 131 }; 132 133 if (proto == IPPROTO_MPTCP) { 134 rta_proto.rta_type = INET_DIAG_REQ_PROTOCOL; 135 rta_proto.rta_len = RTA_LENGTH(sizeof(proto)); 136 137 iov[iovlen++] = (struct iovec){ &rta_proto, sizeof(rta_proto)}; 138 iov[iovlen++] = (struct iovec){ &proto, sizeof(proto)}; 139 req.nlh.nlmsg_len += RTA_LENGTH(sizeof(proto)); 140 } 141 142 struct msghdr msg = { 143 .msg_name = &nladdr, 144 .msg_namelen = sizeof(nladdr), 145 .msg_iov = iov, 146 .msg_iovlen = iovlen 147 }; 148 149 for (;;) { 150 if (sendmsg(fd, &msg, 0) < 0) { 151 if (errno == EINTR) 152 continue; 153 die_perror("sendmsg"); 154 } 155 break; 156 } 157 } 158 159 static void parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta, 160 int len, unsigned short flags) 161 { 162 unsigned short type; 163 164 memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); 165 while (RTA_OK(rta, len)) { 166 type = rta->rta_type & ~flags; 167 if (type <= max && !tb[type]) 168 tb[type] = rta; 169 rta = RTA_NEXT(rta, len); 170 } 171 } 172 173 static void print_info_msg(struct mptcp_info *info) 174 { 175 printf("Token & Flags\n"); 176 printf("token: %x\n", info->mptcpi_token); 177 printf("flags: %x\n", info->mptcpi_flags); 178 printf("csum_enabled: %u\n", info->mptcpi_csum_enabled); 179 180 printf("\nBasic Info\n"); 181 printf("subflows: %u\n", info->mptcpi_subflows); 182 printf("subflows_max: %u\n", info->mptcpi_subflows_max); 183 printf("subflows_total: %u\n", info->mptcpi_subflows_total); 184 printf("local_addr_used: %u\n", info->mptcpi_local_addr_used); 185 printf("local_addr_max: %u\n", info->mptcpi_local_addr_max); 186 printf("add_addr_signal: %u\n", info->mptcpi_add_addr_signal); 187 printf("add_addr_accepted: %u\n", info->mptcpi_add_addr_accepted); 188 printf("add_addr_signal_max: %u\n", info->mptcpi_add_addr_signal_max); 189 printf("add_addr_accepted_max: %u\n", info->mptcpi_add_addr_accepted_max); 190 191 printf("\nTransmission Info\n"); 192 printf("write_seq: %llu\n", info->mptcpi_write_seq); 193 printf("snd_una: %llu\n", info->mptcpi_snd_una); 194 printf("rcv_nxt: %llu\n", info->mptcpi_rcv_nxt); 195 printf("last_data_sent: %u\n", info->mptcpi_last_data_sent); 196 printf("last_data_recv: %u\n", info->mptcpi_last_data_recv); 197 printf("last_ack_recv: %u\n", info->mptcpi_last_ack_recv); 198 printf("retransmits: %u\n", info->mptcpi_retransmits); 199 printf("retransmit bytes: %llu\n", info->mptcpi_bytes_retrans); 200 printf("bytes_sent: %llu\n", info->mptcpi_bytes_sent); 201 printf("bytes_received: %llu\n", info->mptcpi_bytes_received); 202 printf("bytes_acked: %llu\n", info->mptcpi_bytes_acked); 203 } 204 205 /* 206 * 'print_subflow_info' is from 'mptcp_subflow_info' 207 * which is a function in 'misc/ss.c' of iproute2. 208 */ 209 static void print_subflow_info(struct rtattr *tb[]) 210 { 211 u_int32_t flags = 0; 212 213 printf("It's a mptcp subflow, the subflow info:\n"); 214 if (tb[MPTCP_SUBFLOW_ATTR_FLAGS]) { 215 char caps[32 + 1] = { 0 }, *cap = &caps[0]; 216 217 flags = rta_getattr(__u32, tb[MPTCP_SUBFLOW_ATTR_FLAGS]); 218 219 if (flags & MPTCP_SUBFLOW_FLAG_MCAP_REM) 220 *cap++ = 'M'; 221 if (flags & MPTCP_SUBFLOW_FLAG_MCAP_LOC) 222 *cap++ = 'm'; 223 if (flags & MPTCP_SUBFLOW_FLAG_JOIN_REM) 224 *cap++ = 'J'; 225 if (flags & MPTCP_SUBFLOW_FLAG_JOIN_LOC) 226 *cap++ = 'j'; 227 if (flags & MPTCP_SUBFLOW_FLAG_BKUP_REM) 228 *cap++ = 'B'; 229 if (flags & MPTCP_SUBFLOW_FLAG_BKUP_LOC) 230 *cap++ = 'b'; 231 if (flags & MPTCP_SUBFLOW_FLAG_FULLY_ESTABLISHED) 232 *cap++ = 'e'; 233 if (flags & MPTCP_SUBFLOW_FLAG_CONNECTED) 234 *cap++ = 'c'; 235 if (flags & MPTCP_SUBFLOW_FLAG_MAPVALID) 236 *cap++ = 'v'; 237 238 if (flags) 239 printf(" flags:%s", caps); 240 } 241 if (tb[MPTCP_SUBFLOW_ATTR_TOKEN_REM] && 242 tb[MPTCP_SUBFLOW_ATTR_TOKEN_LOC] && 243 tb[MPTCP_SUBFLOW_ATTR_ID_REM] && 244 tb[MPTCP_SUBFLOW_ATTR_ID_LOC]) 245 printf(" token:%04x(id:%u)/%04x(id:%u)", 246 rta_getattr(__u32, tb[MPTCP_SUBFLOW_ATTR_TOKEN_REM]), 247 rta_getattr(__u8, tb[MPTCP_SUBFLOW_ATTR_ID_REM]), 248 rta_getattr(__u32, tb[MPTCP_SUBFLOW_ATTR_TOKEN_LOC]), 249 rta_getattr(__u8, tb[MPTCP_SUBFLOW_ATTR_ID_LOC])); 250 if (tb[MPTCP_SUBFLOW_ATTR_MAP_SEQ]) 251 printf(" seq:%llu", 252 rta_getattr(__u64, tb[MPTCP_SUBFLOW_ATTR_MAP_SEQ])); 253 if (tb[MPTCP_SUBFLOW_ATTR_MAP_SFSEQ]) 254 printf(" sfseq:%u", 255 rta_getattr(__u32, tb[MPTCP_SUBFLOW_ATTR_MAP_SFSEQ])); 256 if (tb[MPTCP_SUBFLOW_ATTR_SSN_OFFSET]) 257 printf(" ssnoff:%u", 258 rta_getattr(__u32, tb[MPTCP_SUBFLOW_ATTR_SSN_OFFSET])); 259 if (tb[MPTCP_SUBFLOW_ATTR_MAP_DATALEN]) 260 printf(" maplen:%u", 261 rta_getattr(__u32, tb[MPTCP_SUBFLOW_ATTR_MAP_DATALEN])); 262 printf("\n"); 263 } 264 265 static void parse_nlmsg(struct nlmsghdr *nlh, __u32 proto) 266 { 267 struct inet_diag_msg *r = NLMSG_DATA(nlh); 268 struct rtattr *tb[INET_DIAG_MAX + 1]; 269 270 parse_rtattr_flags(tb, INET_DIAG_MAX, (struct rtattr *)(r + 1), 271 nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r)), 272 NLA_F_NESTED); 273 274 if (proto == IPPROTO_MPTCP && tb[INET_DIAG_INFO]) { 275 int len = RTA_PAYLOAD(tb[INET_DIAG_INFO]); 276 struct mptcp_info *info; 277 278 /* workaround fort older kernels with less fields */ 279 if (len < sizeof(*info)) { 280 info = alloca(sizeof(*info)); 281 memcpy(info, RTA_DATA(tb[INET_DIAG_INFO]), len); 282 memset((char *)info + len, 0, sizeof(*info) - len); 283 } else { 284 info = RTA_DATA(tb[INET_DIAG_INFO]); 285 } 286 print_info_msg(info); 287 } 288 if (proto == IPPROTO_TCP && tb[INET_DIAG_ULP_INFO]) { 289 struct rtattr *ulpinfo[INET_ULP_INFO_MAX + 1] = { 0 }; 290 291 parse_rtattr_nested(ulpinfo, INET_ULP_INFO_MAX, 292 tb[INET_DIAG_ULP_INFO]); 293 294 if (ulpinfo[INET_ULP_INFO_MPTCP]) { 295 struct rtattr *sfinfo[MPTCP_SUBFLOW_ATTR_MAX + 1] = { 0 }; 296 297 parse_rtattr_nested(sfinfo, MPTCP_SUBFLOW_ATTR_MAX, 298 ulpinfo[INET_ULP_INFO_MPTCP]); 299 print_subflow_info(sfinfo); 300 } else { 301 printf("It's a normal TCP!\n"); 302 } 303 } 304 } 305 306 static void recv_nlmsg(int fd, __u32 proto) 307 { 308 char rcv_buff[8192]; 309 struct nlmsghdr *nlh = (struct nlmsghdr *)rcv_buff; 310 struct sockaddr_nl rcv_nladdr = { 311 .nl_family = AF_NETLINK 312 }; 313 struct iovec rcv_iov = { 314 .iov_base = rcv_buff, 315 .iov_len = sizeof(rcv_buff) 316 }; 317 struct msghdr rcv_msg = { 318 .msg_name = &rcv_nladdr, 319 .msg_namelen = sizeof(rcv_nladdr), 320 .msg_iov = &rcv_iov, 321 .msg_iovlen = 1 322 }; 323 int len; 324 325 len = recvmsg(fd, &rcv_msg, 0); 326 327 while (NLMSG_OK(nlh, len)) { 328 if (nlh->nlmsg_type == NLMSG_DONE) { 329 printf("NLMSG_DONE\n"); 330 break; 331 } else if (nlh->nlmsg_type == NLMSG_ERROR) { 332 struct nlmsgerr *err; 333 334 err = (struct nlmsgerr *)NLMSG_DATA(nlh); 335 printf("Error %d:%s\n", 336 -(err->error), strerror(-(err->error))); 337 break; 338 } 339 parse_nlmsg(nlh, proto); 340 nlh = NLMSG_NEXT(nlh, len); 341 } 342 } 343 344 static void get_mptcpinfo(__u32 token) 345 { 346 struct inet_diag_req_v2 r = { 347 .sdiag_family = AF_INET, 348 /* Real proto is set via INET_DIAG_REQ_PROTOCOL */ 349 .sdiag_protocol = IPPROTO_TCP, 350 .idiag_ext = 1 << (INET_DIAG_INFO - 1), 351 .id.idiag_cookie[0] = token, 352 }; 353 __u32 proto = IPPROTO_MPTCP; 354 int fd; 355 356 fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG); 357 if (fd < 0) 358 die_perror("Netlink socket"); 359 360 send_query(fd, &r, proto); 361 recv_nlmsg(fd, proto); 362 363 close(fd); 364 } 365 366 static void get_subflow_info(char *subflow_addrs) 367 { 368 struct inet_diag_req_v2 r = { 369 .sdiag_family = AF_INET, 370 .sdiag_protocol = IPPROTO_TCP, 371 .idiag_ext = 1 << (INET_DIAG_INFO - 1), 372 .id.idiag_cookie[0] = INET_DIAG_NOCOOKIE, 373 .id.idiag_cookie[1] = INET_DIAG_NOCOOKIE, 374 }; 375 char saddr[64], daddr[64]; 376 int sport, dport; 377 int ret; 378 int fd; 379 380 ret = sscanf(subflow_addrs, "%[^:]:%d %[^:]:%d", saddr, &sport, daddr, &dport); 381 if (ret != 4) 382 die_perror("IP PORT Pairs has style problems!"); 383 384 printf("%s:%d -> %s:%d\n", saddr, sport, daddr, dport); 385 386 fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG); 387 if (fd < 0) 388 die_perror("Netlink socket"); 389 390 r.id.idiag_sport = htons(sport); 391 r.id.idiag_dport = htons(dport); 392 393 inet_pton(AF_INET, saddr, &r.id.idiag_src); 394 inet_pton(AF_INET, daddr, &r.id.idiag_dst); 395 send_query(fd, &r, IPPROTO_TCP); 396 recv_nlmsg(fd, IPPROTO_TCP); 397 } 398 399 static void parse_opts(int argc, char **argv, struct params *p) 400 { 401 int c; 402 403 if (argc < 2) 404 die_usage(1); 405 406 while ((c = getopt(argc, argv, "ht:s:")) != -1) { 407 switch (c) { 408 case 'h': 409 die_usage(0); 410 break; 411 case 't': 412 sscanf(optarg, "%x", &p->target_token); 413 break; 414 case 's': 415 strncpy(p->subflow_addrs, optarg, 416 sizeof(p->subflow_addrs) - 1); 417 break; 418 default: 419 die_usage(1); 420 break; 421 } 422 } 423 } 424 425 int main(int argc, char *argv[]) 426 { 427 struct params p = { 0 }; 428 429 parse_opts(argc, argv, &p); 430 431 if (p.target_token) 432 get_mptcpinfo(p.target_token); 433 434 if (p.subflow_addrs[0] != '\0') 435 get_subflow_info(p.subflow_addrs); 436 437 return 0; 438 } 439 440