18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* SCTP kernel implementation 38c2ecf20Sopenharmony_ci * (C) Copyright Red Hat Inc. 2017 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This file is part of the SCTP kernel implementation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * These functions implement sctp diag support. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Please send any bug reports or fixes you make to the 108c2ecf20Sopenharmony_ci * email addresched(es): 118c2ecf20Sopenharmony_ci * lksctp developers <linux-sctp@vger.kernel.org> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Written or modified by: 148c2ecf20Sopenharmony_ci * Xin Long <lucien.xin@gmail.com> 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/inet_diag.h> 198c2ecf20Sopenharmony_ci#include <linux/sock_diag.h> 208c2ecf20Sopenharmony_ci#include <net/sctp/sctp.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic void sctp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, 238c2ecf20Sopenharmony_ci void *info); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* define some functions to make asoc/ep fill look clean */ 268c2ecf20Sopenharmony_cistatic void inet_diag_msg_sctpasoc_fill(struct inet_diag_msg *r, 278c2ecf20Sopenharmony_ci struct sock *sk, 288c2ecf20Sopenharmony_ci struct sctp_association *asoc) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci union sctp_addr laddr, paddr; 318c2ecf20Sopenharmony_ci struct dst_entry *dst; 328c2ecf20Sopenharmony_ci struct timer_list *t3_rtx = &asoc->peer.primary_path->T3_rtx_timer; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci laddr = list_entry(asoc->base.bind_addr.address_list.next, 358c2ecf20Sopenharmony_ci struct sctp_sockaddr_entry, list)->a; 368c2ecf20Sopenharmony_ci paddr = asoc->peer.primary_path->ipaddr; 378c2ecf20Sopenharmony_ci dst = asoc->peer.primary_path->dst; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci r->idiag_family = sk->sk_family; 408c2ecf20Sopenharmony_ci r->id.idiag_sport = htons(asoc->base.bind_addr.port); 418c2ecf20Sopenharmony_ci r->id.idiag_dport = htons(asoc->peer.port); 428c2ecf20Sopenharmony_ci r->id.idiag_if = dst ? dst->dev->ifindex : 0; 438c2ecf20Sopenharmony_ci sock_diag_save_cookie(sk, r->id.idiag_cookie); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 468c2ecf20Sopenharmony_ci if (sk->sk_family == AF_INET6) { 478c2ecf20Sopenharmony_ci *(struct in6_addr *)r->id.idiag_src = laddr.v6.sin6_addr; 488c2ecf20Sopenharmony_ci *(struct in6_addr *)r->id.idiag_dst = paddr.v6.sin6_addr; 498c2ecf20Sopenharmony_ci } else 508c2ecf20Sopenharmony_ci#endif 518c2ecf20Sopenharmony_ci { 528c2ecf20Sopenharmony_ci memset(&r->id.idiag_src, 0, sizeof(r->id.idiag_src)); 538c2ecf20Sopenharmony_ci memset(&r->id.idiag_dst, 0, sizeof(r->id.idiag_dst)); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci r->id.idiag_src[0] = laddr.v4.sin_addr.s_addr; 568c2ecf20Sopenharmony_ci r->id.idiag_dst[0] = paddr.v4.sin_addr.s_addr; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci r->idiag_state = asoc->state; 608c2ecf20Sopenharmony_ci if (timer_pending(t3_rtx)) { 618c2ecf20Sopenharmony_ci r->idiag_timer = SCTP_EVENT_TIMEOUT_T3_RTX; 628c2ecf20Sopenharmony_ci r->idiag_retrans = asoc->rtx_data_chunks; 638c2ecf20Sopenharmony_ci r->idiag_expires = jiffies_to_msecs(t3_rtx->expires - jiffies); 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int inet_diag_msg_sctpladdrs_fill(struct sk_buff *skb, 688c2ecf20Sopenharmony_ci struct list_head *address_list) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct sctp_sockaddr_entry *laddr; 718c2ecf20Sopenharmony_ci int addrlen = sizeof(struct sockaddr_storage); 728c2ecf20Sopenharmony_ci int addrcnt = 0; 738c2ecf20Sopenharmony_ci struct nlattr *attr; 748c2ecf20Sopenharmony_ci void *info = NULL; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci list_for_each_entry_rcu(laddr, address_list, list) 778c2ecf20Sopenharmony_ci addrcnt++; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci attr = nla_reserve(skb, INET_DIAG_LOCALS, addrlen * addrcnt); 808c2ecf20Sopenharmony_ci if (!attr) 818c2ecf20Sopenharmony_ci return -EMSGSIZE; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci info = nla_data(attr); 848c2ecf20Sopenharmony_ci list_for_each_entry_rcu(laddr, address_list, list) { 858c2ecf20Sopenharmony_ci memcpy(info, &laddr->a, sizeof(laddr->a)); 868c2ecf20Sopenharmony_ci memset(info + sizeof(laddr->a), 0, addrlen - sizeof(laddr->a)); 878c2ecf20Sopenharmony_ci info += addrlen; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return 0; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int inet_diag_msg_sctpaddrs_fill(struct sk_buff *skb, 948c2ecf20Sopenharmony_ci struct sctp_association *asoc) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci int addrlen = sizeof(struct sockaddr_storage); 978c2ecf20Sopenharmony_ci struct sctp_transport *from; 988c2ecf20Sopenharmony_ci struct nlattr *attr; 998c2ecf20Sopenharmony_ci void *info = NULL; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci attr = nla_reserve(skb, INET_DIAG_PEERS, 1028c2ecf20Sopenharmony_ci addrlen * asoc->peer.transport_count); 1038c2ecf20Sopenharmony_ci if (!attr) 1048c2ecf20Sopenharmony_ci return -EMSGSIZE; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci info = nla_data(attr); 1078c2ecf20Sopenharmony_ci list_for_each_entry(from, &asoc->peer.transport_addr_list, 1088c2ecf20Sopenharmony_ci transports) { 1098c2ecf20Sopenharmony_ci memcpy(info, &from->ipaddr, sizeof(from->ipaddr)); 1108c2ecf20Sopenharmony_ci memset(info + sizeof(from->ipaddr), 0, 1118c2ecf20Sopenharmony_ci addrlen - sizeof(from->ipaddr)); 1128c2ecf20Sopenharmony_ci info += addrlen; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* sctp asoc/ep fill*/ 1198c2ecf20Sopenharmony_cistatic int inet_sctp_diag_fill(struct sock *sk, struct sctp_association *asoc, 1208c2ecf20Sopenharmony_ci struct sk_buff *skb, 1218c2ecf20Sopenharmony_ci const struct inet_diag_req_v2 *req, 1228c2ecf20Sopenharmony_ci struct user_namespace *user_ns, 1238c2ecf20Sopenharmony_ci int portid, u32 seq, u16 nlmsg_flags, 1248c2ecf20Sopenharmony_ci const struct nlmsghdr *unlh, 1258c2ecf20Sopenharmony_ci bool net_admin) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct sctp_endpoint *ep = sctp_sk(sk)->ep; 1288c2ecf20Sopenharmony_ci struct list_head *addr_list; 1298c2ecf20Sopenharmony_ci struct inet_diag_msg *r; 1308c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 1318c2ecf20Sopenharmony_ci int ext = req->idiag_ext; 1328c2ecf20Sopenharmony_ci struct sctp_infox infox; 1338c2ecf20Sopenharmony_ci void *info = NULL; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r), 1368c2ecf20Sopenharmony_ci nlmsg_flags); 1378c2ecf20Sopenharmony_ci if (!nlh) 1388c2ecf20Sopenharmony_ci return -EMSGSIZE; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci r = nlmsg_data(nlh); 1418c2ecf20Sopenharmony_ci BUG_ON(!sk_fullsock(sk)); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci r->idiag_timer = 0; 1448c2ecf20Sopenharmony_ci r->idiag_retrans = 0; 1458c2ecf20Sopenharmony_ci r->idiag_expires = 0; 1468c2ecf20Sopenharmony_ci if (asoc) { 1478c2ecf20Sopenharmony_ci inet_diag_msg_sctpasoc_fill(r, sk, asoc); 1488c2ecf20Sopenharmony_ci } else { 1498c2ecf20Sopenharmony_ci inet_diag_msg_common_fill(r, sk); 1508c2ecf20Sopenharmony_ci r->idiag_state = sk->sk_state; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (inet_diag_msg_attrs_fill(sk, skb, r, ext, user_ns, net_admin)) 1548c2ecf20Sopenharmony_ci goto errout; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (ext & (1 << (INET_DIAG_SKMEMINFO - 1))) { 1578c2ecf20Sopenharmony_ci u32 mem[SK_MEMINFO_VARS]; 1588c2ecf20Sopenharmony_ci int amt; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (asoc && asoc->ep->sndbuf_policy) 1618c2ecf20Sopenharmony_ci amt = asoc->sndbuf_used; 1628c2ecf20Sopenharmony_ci else 1638c2ecf20Sopenharmony_ci amt = sk_wmem_alloc_get(sk); 1648c2ecf20Sopenharmony_ci mem[SK_MEMINFO_WMEM_ALLOC] = amt; 1658c2ecf20Sopenharmony_ci if (asoc && asoc->ep->rcvbuf_policy) 1668c2ecf20Sopenharmony_ci amt = atomic_read(&asoc->rmem_alloc); 1678c2ecf20Sopenharmony_ci else 1688c2ecf20Sopenharmony_ci amt = sk_rmem_alloc_get(sk); 1698c2ecf20Sopenharmony_ci mem[SK_MEMINFO_RMEM_ALLOC] = amt; 1708c2ecf20Sopenharmony_ci mem[SK_MEMINFO_RCVBUF] = sk->sk_rcvbuf; 1718c2ecf20Sopenharmony_ci mem[SK_MEMINFO_SNDBUF] = sk->sk_sndbuf; 1728c2ecf20Sopenharmony_ci mem[SK_MEMINFO_FWD_ALLOC] = sk->sk_forward_alloc; 1738c2ecf20Sopenharmony_ci mem[SK_MEMINFO_WMEM_QUEUED] = sk->sk_wmem_queued; 1748c2ecf20Sopenharmony_ci mem[SK_MEMINFO_OPTMEM] = atomic_read(&sk->sk_omem_alloc); 1758c2ecf20Sopenharmony_ci mem[SK_MEMINFO_BACKLOG] = READ_ONCE(sk->sk_backlog.len); 1768c2ecf20Sopenharmony_ci mem[SK_MEMINFO_DROPS] = atomic_read(&sk->sk_drops); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (nla_put(skb, INET_DIAG_SKMEMINFO, sizeof(mem), &mem) < 0) 1798c2ecf20Sopenharmony_ci goto errout; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (ext & (1 << (INET_DIAG_INFO - 1))) { 1838c2ecf20Sopenharmony_ci struct nlattr *attr; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci attr = nla_reserve_64bit(skb, INET_DIAG_INFO, 1868c2ecf20Sopenharmony_ci sizeof(struct sctp_info), 1878c2ecf20Sopenharmony_ci INET_DIAG_PAD); 1888c2ecf20Sopenharmony_ci if (!attr) 1898c2ecf20Sopenharmony_ci goto errout; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci info = nla_data(attr); 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci infox.sctpinfo = (struct sctp_info *)info; 1948c2ecf20Sopenharmony_ci infox.asoc = asoc; 1958c2ecf20Sopenharmony_ci sctp_diag_get_info(sk, r, &infox); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci addr_list = asoc ? &asoc->base.bind_addr.address_list 1988c2ecf20Sopenharmony_ci : &ep->base.bind_addr.address_list; 1998c2ecf20Sopenharmony_ci if (inet_diag_msg_sctpladdrs_fill(skb, addr_list)) 2008c2ecf20Sopenharmony_ci goto errout; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (asoc && (ext & (1 << (INET_DIAG_CONG - 1)))) 2038c2ecf20Sopenharmony_ci if (nla_put_string(skb, INET_DIAG_CONG, "reno") < 0) 2048c2ecf20Sopenharmony_ci goto errout; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (asoc && inet_diag_msg_sctpaddrs_fill(skb, asoc)) 2078c2ecf20Sopenharmony_ci goto errout; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cierrout: 2138c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 2148c2ecf20Sopenharmony_ci return -EMSGSIZE; 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci/* callback and param */ 2188c2ecf20Sopenharmony_cistruct sctp_comm_param { 2198c2ecf20Sopenharmony_ci struct sk_buff *skb; 2208c2ecf20Sopenharmony_ci struct netlink_callback *cb; 2218c2ecf20Sopenharmony_ci const struct inet_diag_req_v2 *r; 2228c2ecf20Sopenharmony_ci const struct nlmsghdr *nlh; 2238c2ecf20Sopenharmony_ci bool net_admin; 2248c2ecf20Sopenharmony_ci}; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic size_t inet_assoc_attr_size(struct sctp_association *asoc) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci int addrlen = sizeof(struct sockaddr_storage); 2298c2ecf20Sopenharmony_ci int addrcnt = 0; 2308c2ecf20Sopenharmony_ci struct sctp_sockaddr_entry *laddr; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci list_for_each_entry_rcu(laddr, &asoc->base.bind_addr.address_list, 2338c2ecf20Sopenharmony_ci list) 2348c2ecf20Sopenharmony_ci addrcnt++; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return nla_total_size(sizeof(struct sctp_info)) 2378c2ecf20Sopenharmony_ci + nla_total_size(addrlen * asoc->peer.transport_count) 2388c2ecf20Sopenharmony_ci + nla_total_size(addrlen * addrcnt) 2398c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct inet_diag_msg)) 2408c2ecf20Sopenharmony_ci + inet_diag_msg_attrs_size() 2418c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct inet_diag_meminfo)) 2428c2ecf20Sopenharmony_ci + 64; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int sctp_tsp_dump_one(struct sctp_transport *tsp, void *p) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct sctp_association *assoc = tsp->asoc; 2488c2ecf20Sopenharmony_ci struct sock *sk = tsp->asoc->base.sk; 2498c2ecf20Sopenharmony_ci struct sctp_comm_param *commp = p; 2508c2ecf20Sopenharmony_ci struct sk_buff *in_skb = commp->skb; 2518c2ecf20Sopenharmony_ci const struct inet_diag_req_v2 *req = commp->r; 2528c2ecf20Sopenharmony_ci const struct nlmsghdr *nlh = commp->nlh; 2538c2ecf20Sopenharmony_ci struct net *net = sock_net(in_skb->sk); 2548c2ecf20Sopenharmony_ci struct sk_buff *rep; 2558c2ecf20Sopenharmony_ci int err; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci err = sock_diag_check_cookie(sk, req->id.idiag_cookie); 2588c2ecf20Sopenharmony_ci if (err) 2598c2ecf20Sopenharmony_ci goto out; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci err = -ENOMEM; 2628c2ecf20Sopenharmony_ci rep = nlmsg_new(inet_assoc_attr_size(assoc), GFP_KERNEL); 2638c2ecf20Sopenharmony_ci if (!rep) 2648c2ecf20Sopenharmony_ci goto out; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci lock_sock(sk); 2678c2ecf20Sopenharmony_ci if (sk != assoc->base.sk) { 2688c2ecf20Sopenharmony_ci release_sock(sk); 2698c2ecf20Sopenharmony_ci sk = assoc->base.sk; 2708c2ecf20Sopenharmony_ci lock_sock(sk); 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci err = inet_sctp_diag_fill(sk, assoc, rep, req, 2738c2ecf20Sopenharmony_ci sk_user_ns(NETLINK_CB(in_skb).sk), 2748c2ecf20Sopenharmony_ci NETLINK_CB(in_skb).portid, 2758c2ecf20Sopenharmony_ci nlh->nlmsg_seq, 0, nlh, 2768c2ecf20Sopenharmony_ci commp->net_admin); 2778c2ecf20Sopenharmony_ci release_sock(sk); 2788c2ecf20Sopenharmony_ci if (err < 0) { 2798c2ecf20Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 2808c2ecf20Sopenharmony_ci kfree_skb(rep); 2818c2ecf20Sopenharmony_ci goto out; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid, 2858c2ecf20Sopenharmony_ci MSG_DONTWAIT); 2868c2ecf20Sopenharmony_ci if (err > 0) 2878c2ecf20Sopenharmony_ci err = 0; 2888c2ecf20Sopenharmony_ciout: 2898c2ecf20Sopenharmony_ci return err; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int sctp_sock_dump(struct sctp_endpoint *ep, struct sctp_transport *tsp, void *p) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct sctp_comm_param *commp = p; 2958c2ecf20Sopenharmony_ci struct sock *sk = ep->base.sk; 2968c2ecf20Sopenharmony_ci struct sk_buff *skb = commp->skb; 2978c2ecf20Sopenharmony_ci struct netlink_callback *cb = commp->cb; 2988c2ecf20Sopenharmony_ci const struct inet_diag_req_v2 *r = commp->r; 2998c2ecf20Sopenharmony_ci struct sctp_association *assoc; 3008c2ecf20Sopenharmony_ci int err = 0; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci lock_sock(sk); 3038c2ecf20Sopenharmony_ci if (ep != tsp->asoc->ep) 3048c2ecf20Sopenharmony_ci goto release; 3058c2ecf20Sopenharmony_ci list_for_each_entry(assoc, &ep->asocs, asocs) { 3068c2ecf20Sopenharmony_ci if (cb->args[4] < cb->args[1]) 3078c2ecf20Sopenharmony_ci goto next; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (r->id.idiag_sport != htons(assoc->base.bind_addr.port) && 3108c2ecf20Sopenharmony_ci r->id.idiag_sport) 3118c2ecf20Sopenharmony_ci goto next; 3128c2ecf20Sopenharmony_ci if (r->id.idiag_dport != htons(assoc->peer.port) && 3138c2ecf20Sopenharmony_ci r->id.idiag_dport) 3148c2ecf20Sopenharmony_ci goto next; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (!cb->args[3] && 3178c2ecf20Sopenharmony_ci inet_sctp_diag_fill(sk, NULL, skb, r, 3188c2ecf20Sopenharmony_ci sk_user_ns(NETLINK_CB(cb->skb).sk), 3198c2ecf20Sopenharmony_ci NETLINK_CB(cb->skb).portid, 3208c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, 3218c2ecf20Sopenharmony_ci NLM_F_MULTI, cb->nlh, 3228c2ecf20Sopenharmony_ci commp->net_admin) < 0) { 3238c2ecf20Sopenharmony_ci err = 1; 3248c2ecf20Sopenharmony_ci goto release; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci cb->args[3] = 1; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (inet_sctp_diag_fill(sk, assoc, skb, r, 3298c2ecf20Sopenharmony_ci sk_user_ns(NETLINK_CB(cb->skb).sk), 3308c2ecf20Sopenharmony_ci NETLINK_CB(cb->skb).portid, 3318c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, 0, cb->nlh, 3328c2ecf20Sopenharmony_ci commp->net_admin) < 0) { 3338c2ecf20Sopenharmony_ci err = 1; 3348c2ecf20Sopenharmony_ci goto release; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_cinext: 3378c2ecf20Sopenharmony_ci cb->args[4]++; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci cb->args[1] = 0; 3408c2ecf20Sopenharmony_ci cb->args[3] = 0; 3418c2ecf20Sopenharmony_ci cb->args[4] = 0; 3428c2ecf20Sopenharmony_cirelease: 3438c2ecf20Sopenharmony_ci release_sock(sk); 3448c2ecf20Sopenharmony_ci return err; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int sctp_sock_filter(struct sctp_endpoint *ep, struct sctp_transport *tsp, void *p) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci struct sctp_comm_param *commp = p; 3508c2ecf20Sopenharmony_ci struct sock *sk = ep->base.sk; 3518c2ecf20Sopenharmony_ci const struct inet_diag_req_v2 *r = commp->r; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* find the ep only once through the transports by this condition */ 3548c2ecf20Sopenharmony_ci if (!list_is_first(&tsp->asoc->asocs, &ep->asocs)) 3558c2ecf20Sopenharmony_ci return 0; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (r->sdiag_family != AF_UNSPEC && sk->sk_family != r->sdiag_family) 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return 1; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int sctp_ep_dump(struct sctp_endpoint *ep, void *p) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct sctp_comm_param *commp = p; 3668c2ecf20Sopenharmony_ci struct sock *sk = ep->base.sk; 3678c2ecf20Sopenharmony_ci struct sk_buff *skb = commp->skb; 3688c2ecf20Sopenharmony_ci struct netlink_callback *cb = commp->cb; 3698c2ecf20Sopenharmony_ci const struct inet_diag_req_v2 *r = commp->r; 3708c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 3718c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 3728c2ecf20Sopenharmony_ci int err = 0; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (!net_eq(sock_net(sk), net)) 3758c2ecf20Sopenharmony_ci goto out; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (cb->args[4] < cb->args[1]) 3788c2ecf20Sopenharmony_ci goto next; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (!(r->idiag_states & TCPF_LISTEN) && !list_empty(&ep->asocs)) 3818c2ecf20Sopenharmony_ci goto next; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (r->sdiag_family != AF_UNSPEC && 3848c2ecf20Sopenharmony_ci sk->sk_family != r->sdiag_family) 3858c2ecf20Sopenharmony_ci goto next; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (r->id.idiag_sport != inet->inet_sport && 3888c2ecf20Sopenharmony_ci r->id.idiag_sport) 3898c2ecf20Sopenharmony_ci goto next; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (r->id.idiag_dport != inet->inet_dport && 3928c2ecf20Sopenharmony_ci r->id.idiag_dport) 3938c2ecf20Sopenharmony_ci goto next; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (inet_sctp_diag_fill(sk, NULL, skb, r, 3968c2ecf20Sopenharmony_ci sk_user_ns(NETLINK_CB(cb->skb).sk), 3978c2ecf20Sopenharmony_ci NETLINK_CB(cb->skb).portid, 3988c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, NLM_F_MULTI, 3998c2ecf20Sopenharmony_ci cb->nlh, commp->net_admin) < 0) { 4008c2ecf20Sopenharmony_ci err = 2; 4018c2ecf20Sopenharmony_ci goto out; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_cinext: 4048c2ecf20Sopenharmony_ci cb->args[4]++; 4058c2ecf20Sopenharmony_ciout: 4068c2ecf20Sopenharmony_ci return err; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci/* define the functions for sctp_diag_handler*/ 4108c2ecf20Sopenharmony_cistatic void sctp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, 4118c2ecf20Sopenharmony_ci void *info) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct sctp_infox *infox = (struct sctp_infox *)info; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (infox->asoc) { 4168c2ecf20Sopenharmony_ci r->idiag_rqueue = atomic_read(&infox->asoc->rmem_alloc); 4178c2ecf20Sopenharmony_ci r->idiag_wqueue = infox->asoc->sndbuf_used; 4188c2ecf20Sopenharmony_ci } else { 4198c2ecf20Sopenharmony_ci r->idiag_rqueue = READ_ONCE(sk->sk_ack_backlog); 4208c2ecf20Sopenharmony_ci r->idiag_wqueue = READ_ONCE(sk->sk_max_ack_backlog); 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci if (infox->sctpinfo) 4238c2ecf20Sopenharmony_ci sctp_get_sctp_info(sk, infox->asoc, infox->sctpinfo); 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic int sctp_diag_dump_one(struct netlink_callback *cb, 4278c2ecf20Sopenharmony_ci const struct inet_diag_req_v2 *req) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci struct sk_buff *in_skb = cb->skb; 4308c2ecf20Sopenharmony_ci struct net *net = sock_net(in_skb->sk); 4318c2ecf20Sopenharmony_ci const struct nlmsghdr *nlh = cb->nlh; 4328c2ecf20Sopenharmony_ci union sctp_addr laddr, paddr; 4338c2ecf20Sopenharmony_ci struct sctp_comm_param commp = { 4348c2ecf20Sopenharmony_ci .skb = in_skb, 4358c2ecf20Sopenharmony_ci .r = req, 4368c2ecf20Sopenharmony_ci .nlh = nlh, 4378c2ecf20Sopenharmony_ci .net_admin = netlink_net_capable(in_skb, CAP_NET_ADMIN), 4388c2ecf20Sopenharmony_ci }; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (req->sdiag_family == AF_INET) { 4418c2ecf20Sopenharmony_ci laddr.v4.sin_port = req->id.idiag_sport; 4428c2ecf20Sopenharmony_ci laddr.v4.sin_addr.s_addr = req->id.idiag_src[0]; 4438c2ecf20Sopenharmony_ci laddr.v4.sin_family = AF_INET; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci paddr.v4.sin_port = req->id.idiag_dport; 4468c2ecf20Sopenharmony_ci paddr.v4.sin_addr.s_addr = req->id.idiag_dst[0]; 4478c2ecf20Sopenharmony_ci paddr.v4.sin_family = AF_INET; 4488c2ecf20Sopenharmony_ci } else { 4498c2ecf20Sopenharmony_ci laddr.v6.sin6_port = req->id.idiag_sport; 4508c2ecf20Sopenharmony_ci memcpy(&laddr.v6.sin6_addr, req->id.idiag_src, 4518c2ecf20Sopenharmony_ci sizeof(laddr.v6.sin6_addr)); 4528c2ecf20Sopenharmony_ci laddr.v6.sin6_family = AF_INET6; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci paddr.v6.sin6_port = req->id.idiag_dport; 4558c2ecf20Sopenharmony_ci memcpy(&paddr.v6.sin6_addr, req->id.idiag_dst, 4568c2ecf20Sopenharmony_ci sizeof(paddr.v6.sin6_addr)); 4578c2ecf20Sopenharmony_ci paddr.v6.sin6_family = AF_INET6; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci return sctp_transport_lookup_process(sctp_tsp_dump_one, 4618c2ecf20Sopenharmony_ci net, &laddr, &paddr, &commp); 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic void sctp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, 4658c2ecf20Sopenharmony_ci const struct inet_diag_req_v2 *r) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci u32 idiag_states = r->idiag_states; 4688c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 4698c2ecf20Sopenharmony_ci struct sctp_comm_param commp = { 4708c2ecf20Sopenharmony_ci .skb = skb, 4718c2ecf20Sopenharmony_ci .cb = cb, 4728c2ecf20Sopenharmony_ci .r = r, 4738c2ecf20Sopenharmony_ci .net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN), 4748c2ecf20Sopenharmony_ci }; 4758c2ecf20Sopenharmony_ci int pos = cb->args[2]; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* eps hashtable dumps 4788c2ecf20Sopenharmony_ci * args: 4798c2ecf20Sopenharmony_ci * 0 : if it will traversal listen sock 4808c2ecf20Sopenharmony_ci * 1 : to record the sock pos of this time's traversal 4818c2ecf20Sopenharmony_ci * 4 : to work as a temporary variable to traversal list 4828c2ecf20Sopenharmony_ci */ 4838c2ecf20Sopenharmony_ci if (cb->args[0] == 0) { 4848c2ecf20Sopenharmony_ci if (!(idiag_states & TCPF_LISTEN)) 4858c2ecf20Sopenharmony_ci goto skip; 4868c2ecf20Sopenharmony_ci if (sctp_for_each_endpoint(sctp_ep_dump, &commp)) 4878c2ecf20Sopenharmony_ci goto done; 4888c2ecf20Sopenharmony_ciskip: 4898c2ecf20Sopenharmony_ci cb->args[0] = 1; 4908c2ecf20Sopenharmony_ci cb->args[1] = 0; 4918c2ecf20Sopenharmony_ci cb->args[4] = 0; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci /* asocs by transport hashtable dump 4958c2ecf20Sopenharmony_ci * args: 4968c2ecf20Sopenharmony_ci * 1 : to record the assoc pos of this time's traversal 4978c2ecf20Sopenharmony_ci * 2 : to record the transport pos of this time's traversal 4988c2ecf20Sopenharmony_ci * 3 : to mark if we have dumped the ep info of the current asoc 4998c2ecf20Sopenharmony_ci * 4 : to work as a temporary variable to traversal list 5008c2ecf20Sopenharmony_ci * 5 : to save the sk we get from travelsing the tsp list. 5018c2ecf20Sopenharmony_ci */ 5028c2ecf20Sopenharmony_ci if (!(idiag_states & ~(TCPF_LISTEN | TCPF_CLOSE))) 5038c2ecf20Sopenharmony_ci goto done; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci sctp_transport_traverse_process(sctp_sock_filter, sctp_sock_dump, 5068c2ecf20Sopenharmony_ci net, &pos, &commp); 5078c2ecf20Sopenharmony_ci cb->args[2] = pos; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cidone: 5108c2ecf20Sopenharmony_ci cb->args[1] = cb->args[4]; 5118c2ecf20Sopenharmony_ci cb->args[4] = 0; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic const struct inet_diag_handler sctp_diag_handler = { 5158c2ecf20Sopenharmony_ci .dump = sctp_diag_dump, 5168c2ecf20Sopenharmony_ci .dump_one = sctp_diag_dump_one, 5178c2ecf20Sopenharmony_ci .idiag_get_info = sctp_diag_get_info, 5188c2ecf20Sopenharmony_ci .idiag_type = IPPROTO_SCTP, 5198c2ecf20Sopenharmony_ci .idiag_info_size = sizeof(struct sctp_info), 5208c2ecf20Sopenharmony_ci}; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic int __init sctp_diag_init(void) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci return inet_diag_register(&sctp_diag_handler); 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic void __exit sctp_diag_exit(void) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci inet_diag_unregister(&sctp_diag_handler); 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cimodule_init(sctp_diag_init); 5338c2ecf20Sopenharmony_cimodule_exit(sctp_diag_exit); 5348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5358c2ecf20Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2-132); 536