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