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 12 #include <unistd.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <errno.h> 16 #include <stdio.h> 17 18 #ifndef IPPROTO_MPTCP 19 #define IPPROTO_MPTCP 262 20 #endif 21 22 struct params { 23 __u32 target_token; 24 }; 25 26 struct mptcp_info { 27 __u8 mptcpi_subflows; 28 __u8 mptcpi_add_addr_signal; 29 __u8 mptcpi_add_addr_accepted; 30 __u8 mptcpi_subflows_max; 31 __u8 mptcpi_add_addr_signal_max; 32 __u8 mptcpi_add_addr_accepted_max; 33 __u32 mptcpi_flags; 34 __u32 mptcpi_token; 35 __u64 mptcpi_write_seq; 36 __u64 mptcpi_snd_una; 37 __u64 mptcpi_rcv_nxt; 38 __u8 mptcpi_local_addr_used; 39 __u8 mptcpi_local_addr_max; 40 __u8 mptcpi_csum_enabled; 41 __u32 mptcpi_retransmits; 42 __u64 mptcpi_bytes_retrans; 43 __u64 mptcpi_bytes_sent; 44 __u64 mptcpi_bytes_received; 45 __u64 mptcpi_bytes_acked; 46 __u8 mptcpi_subflows_total; 47 __u8 reserved[3]; 48 __u32 mptcpi_last_data_sent; 49 __u32 mptcpi_last_data_recv; 50 __u32 mptcpi_last_ack_recv; 51 }; 52 53 static void die_perror(const char *msg) 54 { 55 perror(msg); 56 exit(1); 57 } 58 59 static void die_usage(int r) 60 { 61 fprintf(stderr, "Usage: mptcp_diag -t\n"); 62 exit(r); 63 } 64 65 static void send_query(int fd, struct inet_diag_req_v2 *r, __u32 proto) 66 { 67 struct sockaddr_nl nladdr = { 68 .nl_family = AF_NETLINK 69 }; 70 struct { 71 struct nlmsghdr nlh; 72 struct inet_diag_req_v2 r; 73 } req = { 74 .nlh = { 75 .nlmsg_len = sizeof(req), 76 .nlmsg_type = SOCK_DIAG_BY_FAMILY, 77 .nlmsg_flags = NLM_F_REQUEST 78 }, 79 .r = *r 80 }; 81 struct rtattr rta_proto; 82 struct iovec iov[6]; 83 int iovlen = 0; 84 85 iov[iovlen++] = (struct iovec) { 86 .iov_base = &req, 87 .iov_len = sizeof(req) 88 }; 89 90 if (proto == IPPROTO_MPTCP) { 91 rta_proto.rta_type = INET_DIAG_REQ_PROTOCOL; 92 rta_proto.rta_len = RTA_LENGTH(sizeof(proto)); 93 94 iov[iovlen++] = (struct iovec){ &rta_proto, sizeof(rta_proto)}; 95 iov[iovlen++] = (struct iovec){ &proto, sizeof(proto)}; 96 req.nlh.nlmsg_len += RTA_LENGTH(sizeof(proto)); 97 } 98 99 struct msghdr msg = { 100 .msg_name = &nladdr, 101 .msg_namelen = sizeof(nladdr), 102 .msg_iov = iov, 103 .msg_iovlen = iovlen 104 }; 105 106 for (;;) { 107 if (sendmsg(fd, &msg, 0) < 0) { 108 if (errno == EINTR) 109 continue; 110 die_perror("sendmsg"); 111 } 112 break; 113 } 114 } 115 116 static void parse_rtattr_flags(struct rtattr *tb[], int max, struct rtattr *rta, 117 int len, unsigned short flags) 118 { 119 unsigned short type; 120 121 memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); 122 while (RTA_OK(rta, len)) { 123 type = rta->rta_type & ~flags; 124 if (type <= max && !tb[type]) 125 tb[type] = rta; 126 rta = RTA_NEXT(rta, len); 127 } 128 } 129 130 static void print_info_msg(struct mptcp_info *info) 131 { 132 printf("Token & Flags\n"); 133 printf("token: %x\n", info->mptcpi_token); 134 printf("flags: %x\n", info->mptcpi_flags); 135 printf("csum_enabled: %u\n", info->mptcpi_csum_enabled); 136 137 printf("\nBasic Info\n"); 138 printf("subflows: %u\n", info->mptcpi_subflows); 139 printf("subflows_max: %u\n", info->mptcpi_subflows_max); 140 printf("subflows_total: %u\n", info->mptcpi_subflows_total); 141 printf("local_addr_used: %u\n", info->mptcpi_local_addr_used); 142 printf("local_addr_max: %u\n", info->mptcpi_local_addr_max); 143 printf("add_addr_signal: %u\n", info->mptcpi_add_addr_signal); 144 printf("add_addr_accepted: %u\n", info->mptcpi_add_addr_accepted); 145 printf("add_addr_signal_max: %u\n", info->mptcpi_add_addr_signal_max); 146 printf("add_addr_accepted_max: %u\n", info->mptcpi_add_addr_accepted_max); 147 148 printf("\nTransmission Info\n"); 149 printf("write_seq: %llu\n", info->mptcpi_write_seq); 150 printf("snd_una: %llu\n", info->mptcpi_snd_una); 151 printf("rcv_nxt: %llu\n", info->mptcpi_rcv_nxt); 152 printf("last_data_sent: %u\n", info->mptcpi_last_data_sent); 153 printf("last_data_recv: %u\n", info->mptcpi_last_data_recv); 154 printf("last_ack_recv: %u\n", info->mptcpi_last_ack_recv); 155 printf("retransmits: %u\n", info->mptcpi_retransmits); 156 printf("retransmit bytes: %llu\n", info->mptcpi_bytes_retrans); 157 printf("bytes_sent: %llu\n", info->mptcpi_bytes_sent); 158 printf("bytes_received: %llu\n", info->mptcpi_bytes_received); 159 printf("bytes_acked: %llu\n", info->mptcpi_bytes_acked); 160 } 161 162 static void parse_nlmsg(struct nlmsghdr *nlh, __u32 proto) 163 { 164 struct inet_diag_msg *r = NLMSG_DATA(nlh); 165 struct rtattr *tb[INET_DIAG_MAX + 1]; 166 167 parse_rtattr_flags(tb, INET_DIAG_MAX, (struct rtattr *)(r + 1), 168 nlh->nlmsg_len - NLMSG_LENGTH(sizeof(*r)), 169 NLA_F_NESTED); 170 171 if (proto == IPPROTO_MPTCP && tb[INET_DIAG_INFO]) { 172 int len = RTA_PAYLOAD(tb[INET_DIAG_INFO]); 173 struct mptcp_info *info; 174 175 /* workaround fort older kernels with less fields */ 176 if (len < sizeof(*info)) { 177 info = alloca(sizeof(*info)); 178 memcpy(info, RTA_DATA(tb[INET_DIAG_INFO]), len); 179 memset((char *)info + len, 0, sizeof(*info) - len); 180 } else { 181 info = RTA_DATA(tb[INET_DIAG_INFO]); 182 } 183 print_info_msg(info); 184 } 185 } 186 187 static void recv_nlmsg(int fd, __u32 proto) 188 { 189 char rcv_buff[8192]; 190 struct nlmsghdr *nlh = (struct nlmsghdr *)rcv_buff; 191 struct sockaddr_nl rcv_nladdr = { 192 .nl_family = AF_NETLINK 193 }; 194 struct iovec rcv_iov = { 195 .iov_base = rcv_buff, 196 .iov_len = sizeof(rcv_buff) 197 }; 198 struct msghdr rcv_msg = { 199 .msg_name = &rcv_nladdr, 200 .msg_namelen = sizeof(rcv_nladdr), 201 .msg_iov = &rcv_iov, 202 .msg_iovlen = 1 203 }; 204 int len; 205 206 len = recvmsg(fd, &rcv_msg, 0); 207 208 while (NLMSG_OK(nlh, len)) { 209 if (nlh->nlmsg_type == NLMSG_DONE) { 210 printf("NLMSG_DONE\n"); 211 break; 212 } else if (nlh->nlmsg_type == NLMSG_ERROR) { 213 struct nlmsgerr *err; 214 215 err = (struct nlmsgerr *)NLMSG_DATA(nlh); 216 printf("Error %d:%s\n", 217 -(err->error), strerror(-(err->error))); 218 break; 219 } 220 parse_nlmsg(nlh, proto); 221 nlh = NLMSG_NEXT(nlh, len); 222 } 223 } 224 225 static void get_mptcpinfo(__u32 token) 226 { 227 struct inet_diag_req_v2 r = { 228 .sdiag_family = AF_INET, 229 /* Real proto is set via INET_DIAG_REQ_PROTOCOL */ 230 .sdiag_protocol = IPPROTO_TCP, 231 .idiag_ext = 1 << (INET_DIAG_INFO - 1), 232 .id.idiag_cookie[0] = token, 233 }; 234 __u32 proto = IPPROTO_MPTCP; 235 int fd; 236 237 fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG); 238 if (fd < 0) 239 die_perror("Netlink socket"); 240 241 send_query(fd, &r, proto); 242 recv_nlmsg(fd, proto); 243 244 close(fd); 245 } 246 247 static void parse_opts(int argc, char **argv, struct params *p) 248 { 249 int c; 250 251 if (argc < 2) 252 die_usage(1); 253 254 while ((c = getopt(argc, argv, "ht:")) != -1) { 255 switch (c) { 256 case 'h': 257 die_usage(0); 258 break; 259 case 't': 260 sscanf(optarg, "%x", &p->target_token); 261 break; 262 default: 263 die_usage(1); 264 break; 265 } 266 } 267 } 268 269 int main(int argc, char *argv[]) 270 { 271 struct params p = { 0 }; 272 273 parse_opts(argc, argv, &p); 274 275 if (p.target_token) 276 get_mptcpinfo(p.target_token); 277 278 return 0; 279 } 280 281