18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* MPTCP socket monitoring support 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2020 Red Hat 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Author: Paolo Abeni <pabeni@redhat.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/net.h> 118c2ecf20Sopenharmony_ci#include <linux/inet_diag.h> 128c2ecf20Sopenharmony_ci#include <net/netlink.h> 138c2ecf20Sopenharmony_ci#include <uapi/linux/mptcp.h> 148c2ecf20Sopenharmony_ci#include "protocol.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic int sk_diag_dump(struct sock *sk, struct sk_buff *skb, 178c2ecf20Sopenharmony_ci struct netlink_callback *cb, 188c2ecf20Sopenharmony_ci const struct inet_diag_req_v2 *req, 198c2ecf20Sopenharmony_ci struct nlattr *bc, bool net_admin) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci if (!inet_diag_bc_sk(bc, sk)) 228c2ecf20Sopenharmony_ci return 0; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci return inet_sk_diag_fill(sk, inet_csk(sk), skb, cb, req, NLM_F_MULTI, 258c2ecf20Sopenharmony_ci net_admin); 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int mptcp_diag_dump_one(struct netlink_callback *cb, 298c2ecf20Sopenharmony_ci const struct inet_diag_req_v2 *req) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct sk_buff *in_skb = cb->skb; 328c2ecf20Sopenharmony_ci struct mptcp_sock *msk = NULL; 338c2ecf20Sopenharmony_ci struct sk_buff *rep; 348c2ecf20Sopenharmony_ci int err = -ENOENT; 358c2ecf20Sopenharmony_ci struct net *net; 368c2ecf20Sopenharmony_ci struct sock *sk; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci net = sock_net(in_skb->sk); 398c2ecf20Sopenharmony_ci msk = mptcp_token_get_sock(net, req->id.idiag_cookie[0]); 408c2ecf20Sopenharmony_ci if (!msk) 418c2ecf20Sopenharmony_ci goto out_nosk; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci err = -ENOMEM; 448c2ecf20Sopenharmony_ci sk = (struct sock *)msk; 458c2ecf20Sopenharmony_ci rep = nlmsg_new(nla_total_size(sizeof(struct inet_diag_msg)) + 468c2ecf20Sopenharmony_ci inet_diag_msg_attrs_size() + 478c2ecf20Sopenharmony_ci nla_total_size(sizeof(struct mptcp_info)) + 488c2ecf20Sopenharmony_ci nla_total_size(sizeof(struct inet_diag_meminfo)) + 64, 498c2ecf20Sopenharmony_ci GFP_KERNEL); 508c2ecf20Sopenharmony_ci if (!rep) 518c2ecf20Sopenharmony_ci goto out; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci err = inet_sk_diag_fill(sk, inet_csk(sk), rep, cb, req, 0, 548c2ecf20Sopenharmony_ci netlink_net_capable(in_skb, CAP_NET_ADMIN)); 558c2ecf20Sopenharmony_ci if (err < 0) { 568c2ecf20Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 578c2ecf20Sopenharmony_ci kfree_skb(rep); 588c2ecf20Sopenharmony_ci goto out; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid, 618c2ecf20Sopenharmony_ci MSG_DONTWAIT); 628c2ecf20Sopenharmony_ci if (err > 0) 638c2ecf20Sopenharmony_ci err = 0; 648c2ecf20Sopenharmony_ciout: 658c2ecf20Sopenharmony_ci sock_put(sk); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ciout_nosk: 688c2ecf20Sopenharmony_ci return err; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic void mptcp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, 728c2ecf20Sopenharmony_ci const struct inet_diag_req_v2 *r) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci bool net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN); 758c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 768c2ecf20Sopenharmony_ci struct inet_diag_dump_data *cb_data; 778c2ecf20Sopenharmony_ci struct mptcp_sock *msk; 788c2ecf20Sopenharmony_ci struct nlattr *bc; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci cb_data = cb->data; 818c2ecf20Sopenharmony_ci bc = cb_data->inet_diag_nla_bc; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci while ((msk = mptcp_token_iter_next(net, &cb->args[0], &cb->args[1])) != 848c2ecf20Sopenharmony_ci NULL) { 858c2ecf20Sopenharmony_ci struct inet_sock *inet = (struct inet_sock *)msk; 868c2ecf20Sopenharmony_ci struct sock *sk = (struct sock *)msk; 878c2ecf20Sopenharmony_ci int ret = 0; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (!(r->idiag_states & (1 << sk->sk_state))) 908c2ecf20Sopenharmony_ci goto next; 918c2ecf20Sopenharmony_ci if (r->sdiag_family != AF_UNSPEC && 928c2ecf20Sopenharmony_ci sk->sk_family != r->sdiag_family) 938c2ecf20Sopenharmony_ci goto next; 948c2ecf20Sopenharmony_ci if (r->id.idiag_sport != inet->inet_sport && 958c2ecf20Sopenharmony_ci r->id.idiag_sport) 968c2ecf20Sopenharmony_ci goto next; 978c2ecf20Sopenharmony_ci if (r->id.idiag_dport != inet->inet_dport && 988c2ecf20Sopenharmony_ci r->id.idiag_dport) 998c2ecf20Sopenharmony_ci goto next; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci ret = sk_diag_dump(sk, skb, cb, r, bc, net_admin); 1028c2ecf20Sopenharmony_cinext: 1038c2ecf20Sopenharmony_ci sock_put(sk); 1048c2ecf20Sopenharmony_ci if (ret < 0) { 1058c2ecf20Sopenharmony_ci /* will retry on the same position */ 1068c2ecf20Sopenharmony_ci cb->args[1]--; 1078c2ecf20Sopenharmony_ci break; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci cond_resched(); 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic void mptcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, 1148c2ecf20Sopenharmony_ci void *_info) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct mptcp_sock *msk = mptcp_sk(sk); 1178c2ecf20Sopenharmony_ci struct mptcp_info *info = _info; 1188c2ecf20Sopenharmony_ci u32 flags = 0; 1198c2ecf20Sopenharmony_ci bool slow; 1208c2ecf20Sopenharmony_ci u8 val; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci r->idiag_rqueue = sk_rmem_alloc_get(sk); 1238c2ecf20Sopenharmony_ci r->idiag_wqueue = sk_wmem_alloc_get(sk); 1248c2ecf20Sopenharmony_ci if (!info) 1258c2ecf20Sopenharmony_ci return; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci slow = lock_sock_fast(sk); 1288c2ecf20Sopenharmony_ci info->mptcpi_subflows = READ_ONCE(msk->pm.subflows); 1298c2ecf20Sopenharmony_ci info->mptcpi_add_addr_signal = READ_ONCE(msk->pm.add_addr_signaled); 1308c2ecf20Sopenharmony_ci info->mptcpi_add_addr_accepted = READ_ONCE(msk->pm.add_addr_accepted); 1318c2ecf20Sopenharmony_ci info->mptcpi_subflows_max = READ_ONCE(msk->pm.subflows_max); 1328c2ecf20Sopenharmony_ci val = READ_ONCE(msk->pm.add_addr_signal_max); 1338c2ecf20Sopenharmony_ci info->mptcpi_add_addr_signal_max = val; 1348c2ecf20Sopenharmony_ci val = READ_ONCE(msk->pm.add_addr_accept_max); 1358c2ecf20Sopenharmony_ci info->mptcpi_add_addr_accepted_max = val; 1368c2ecf20Sopenharmony_ci if (test_bit(MPTCP_FALLBACK_DONE, &msk->flags)) 1378c2ecf20Sopenharmony_ci flags |= MPTCP_INFO_FLAG_FALLBACK; 1388c2ecf20Sopenharmony_ci if (READ_ONCE(msk->can_ack)) 1398c2ecf20Sopenharmony_ci flags |= MPTCP_INFO_FLAG_REMOTE_KEY_RECEIVED; 1408c2ecf20Sopenharmony_ci info->mptcpi_flags = flags; 1418c2ecf20Sopenharmony_ci info->mptcpi_token = READ_ONCE(msk->token); 1428c2ecf20Sopenharmony_ci info->mptcpi_write_seq = READ_ONCE(msk->write_seq); 1438c2ecf20Sopenharmony_ci info->mptcpi_snd_una = atomic64_read(&msk->snd_una); 1448c2ecf20Sopenharmony_ci info->mptcpi_rcv_nxt = READ_ONCE(msk->ack_seq); 1458c2ecf20Sopenharmony_ci unlock_sock_fast(sk, slow); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic const struct inet_diag_handler mptcp_diag_handler = { 1498c2ecf20Sopenharmony_ci .dump = mptcp_diag_dump, 1508c2ecf20Sopenharmony_ci .dump_one = mptcp_diag_dump_one, 1518c2ecf20Sopenharmony_ci .idiag_get_info = mptcp_diag_get_info, 1528c2ecf20Sopenharmony_ci .idiag_type = IPPROTO_MPTCP, 1538c2ecf20Sopenharmony_ci .idiag_info_size = sizeof(struct mptcp_info), 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic int __init mptcp_diag_init(void) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci return inet_diag_register(&mptcp_diag_handler); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic void __exit mptcp_diag_exit(void) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci inet_diag_unregister(&mptcp_diag_handler); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cimodule_init(mptcp_diag_init); 1678c2ecf20Sopenharmony_cimodule_exit(mptcp_diag_exit); 1688c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1698c2ecf20Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-262 /* AF_INET - IPPROTO_MPTCP */); 170