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