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