18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * inet_diag.c Module for monitoring INET transport protocols sockets. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 128c2ecf20Sopenharmony_ci#include <linux/random.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/cache.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/time.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <net/icmp.h> 198c2ecf20Sopenharmony_ci#include <net/tcp.h> 208c2ecf20Sopenharmony_ci#include <net/ipv6.h> 218c2ecf20Sopenharmony_ci#include <net/inet_common.h> 228c2ecf20Sopenharmony_ci#include <net/inet_connection_sock.h> 238c2ecf20Sopenharmony_ci#include <net/inet_hashtables.h> 248c2ecf20Sopenharmony_ci#include <net/inet_timewait_sock.h> 258c2ecf20Sopenharmony_ci#include <net/inet6_hashtables.h> 268c2ecf20Sopenharmony_ci#include <net/bpf_sk_storage.h> 278c2ecf20Sopenharmony_ci#include <net/netlink.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include <linux/inet.h> 308c2ecf20Sopenharmony_ci#include <linux/stddef.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include <linux/inet_diag.h> 338c2ecf20Sopenharmony_ci#include <linux/sock_diag.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic const struct inet_diag_handler **inet_diag_table; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct inet_diag_entry { 388c2ecf20Sopenharmony_ci const __be32 *saddr; 398c2ecf20Sopenharmony_ci const __be32 *daddr; 408c2ecf20Sopenharmony_ci u16 sport; 418c2ecf20Sopenharmony_ci u16 dport; 428c2ecf20Sopenharmony_ci u16 family; 438c2ecf20Sopenharmony_ci u16 userlocks; 448c2ecf20Sopenharmony_ci u32 ifindex; 458c2ecf20Sopenharmony_ci u32 mark; 468c2ecf20Sopenharmony_ci#ifdef CONFIG_SOCK_CGROUP_DATA 478c2ecf20Sopenharmony_ci u64 cgroup_id; 488c2ecf20Sopenharmony_ci#endif 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(inet_diag_table_mutex); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic const struct inet_diag_handler *inet_diag_lock_handler(int proto) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci if (proto < 0 || proto >= IPPROTO_MAX) { 568c2ecf20Sopenharmony_ci mutex_lock(&inet_diag_table_mutex); 578c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (!inet_diag_table[proto]) 618c2ecf20Sopenharmony_ci sock_load_diag_module(AF_INET, proto); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci mutex_lock(&inet_diag_table_mutex); 648c2ecf20Sopenharmony_ci if (!inet_diag_table[proto]) 658c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return inet_diag_table[proto]; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic void inet_diag_unlock_handler(const struct inet_diag_handler *handler) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci mutex_unlock(&inet_diag_table_mutex); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_civoid inet_diag_msg_common_fill(struct inet_diag_msg *r, struct sock *sk) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci r->idiag_family = sk->sk_family; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci r->id.idiag_sport = htons(sk->sk_num); 808c2ecf20Sopenharmony_ci r->id.idiag_dport = sk->sk_dport; 818c2ecf20Sopenharmony_ci r->id.idiag_if = sk->sk_bound_dev_if; 828c2ecf20Sopenharmony_ci sock_diag_save_cookie(sk, r->id.idiag_cookie); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 858c2ecf20Sopenharmony_ci if (sk->sk_family == AF_INET6) { 868c2ecf20Sopenharmony_ci *(struct in6_addr *)r->id.idiag_src = sk->sk_v6_rcv_saddr; 878c2ecf20Sopenharmony_ci *(struct in6_addr *)r->id.idiag_dst = sk->sk_v6_daddr; 888c2ecf20Sopenharmony_ci } else 898c2ecf20Sopenharmony_ci#endif 908c2ecf20Sopenharmony_ci { 918c2ecf20Sopenharmony_ci memset(&r->id.idiag_src, 0, sizeof(r->id.idiag_src)); 928c2ecf20Sopenharmony_ci memset(&r->id.idiag_dst, 0, sizeof(r->id.idiag_dst)); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci r->id.idiag_src[0] = sk->sk_rcv_saddr; 958c2ecf20Sopenharmony_ci r->id.idiag_dst[0] = sk->sk_daddr; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_diag_msg_common_fill); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic size_t inet_sk_attr_size(struct sock *sk, 1018c2ecf20Sopenharmony_ci const struct inet_diag_req_v2 *req, 1028c2ecf20Sopenharmony_ci bool net_admin) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci const struct inet_diag_handler *handler; 1058c2ecf20Sopenharmony_ci size_t aux = 0; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci handler = inet_diag_table[req->sdiag_protocol]; 1088c2ecf20Sopenharmony_ci if (handler && handler->idiag_get_aux_size) 1098c2ecf20Sopenharmony_ci aux = handler->idiag_get_aux_size(sk, net_admin); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return nla_total_size(sizeof(struct tcp_info)) 1128c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct inet_diag_msg)) 1138c2ecf20Sopenharmony_ci + inet_diag_msg_attrs_size() 1148c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct inet_diag_meminfo)) 1158c2ecf20Sopenharmony_ci + nla_total_size(SK_MEMINFO_VARS * sizeof(u32)) 1168c2ecf20Sopenharmony_ci + nla_total_size(TCP_CA_NAME_MAX) 1178c2ecf20Sopenharmony_ci + nla_total_size(sizeof(struct tcpvegas_info)) 1188c2ecf20Sopenharmony_ci + aux 1198c2ecf20Sopenharmony_ci + 64; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ciint inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb, 1238c2ecf20Sopenharmony_ci struct inet_diag_msg *r, int ext, 1248c2ecf20Sopenharmony_ci struct user_namespace *user_ns, 1258c2ecf20Sopenharmony_ci bool net_admin) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci const struct inet_sock *inet = inet_sk(sk); 1288c2ecf20Sopenharmony_ci struct inet_diag_sockopt inet_sockopt; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (nla_put_u8(skb, INET_DIAG_SHUTDOWN, sk->sk_shutdown)) 1318c2ecf20Sopenharmony_ci goto errout; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci /* IPv6 dual-stack sockets use inet->tos for IPv4 connections, 1348c2ecf20Sopenharmony_ci * hence this needs to be included regardless of socket family. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ci if (ext & (1 << (INET_DIAG_TOS - 1))) 1378c2ecf20Sopenharmony_ci if (nla_put_u8(skb, INET_DIAG_TOS, inet->tos) < 0) 1388c2ecf20Sopenharmony_ci goto errout; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 1418c2ecf20Sopenharmony_ci if (r->idiag_family == AF_INET6) { 1428c2ecf20Sopenharmony_ci if (ext & (1 << (INET_DIAG_TCLASS - 1))) 1438c2ecf20Sopenharmony_ci if (nla_put_u8(skb, INET_DIAG_TCLASS, 1448c2ecf20Sopenharmony_ci inet6_sk(sk)->tclass) < 0) 1458c2ecf20Sopenharmony_ci goto errout; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) && 1488c2ecf20Sopenharmony_ci nla_put_u8(skb, INET_DIAG_SKV6ONLY, ipv6_only_sock(sk))) 1498c2ecf20Sopenharmony_ci goto errout; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci#endif 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (net_admin && nla_put_u32(skb, INET_DIAG_MARK, sk->sk_mark)) 1548c2ecf20Sopenharmony_ci goto errout; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (ext & (1 << (INET_DIAG_CLASS_ID - 1)) || 1578c2ecf20Sopenharmony_ci ext & (1 << (INET_DIAG_TCLASS - 1))) { 1588c2ecf20Sopenharmony_ci u32 classid = 0; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci#ifdef CONFIG_SOCK_CGROUP_DATA 1618c2ecf20Sopenharmony_ci classid = sock_cgroup_classid(&sk->sk_cgrp_data); 1628c2ecf20Sopenharmony_ci#endif 1638c2ecf20Sopenharmony_ci /* Fallback to socket priority if class id isn't set. 1648c2ecf20Sopenharmony_ci * Classful qdiscs use it as direct reference to class. 1658c2ecf20Sopenharmony_ci * For cgroup2 classid is always zero. 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci if (!classid) 1688c2ecf20Sopenharmony_ci classid = sk->sk_priority; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (nla_put_u32(skb, INET_DIAG_CLASS_ID, classid)) 1718c2ecf20Sopenharmony_ci goto errout; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci#ifdef CONFIG_SOCK_CGROUP_DATA 1758c2ecf20Sopenharmony_ci if (nla_put_u64_64bit(skb, INET_DIAG_CGROUP_ID, 1768c2ecf20Sopenharmony_ci cgroup_id(sock_cgroup_ptr(&sk->sk_cgrp_data)), 1778c2ecf20Sopenharmony_ci INET_DIAG_PAD)) 1788c2ecf20Sopenharmony_ci goto errout; 1798c2ecf20Sopenharmony_ci#endif 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci r->idiag_uid = from_kuid_munged(user_ns, sock_i_uid(sk)); 1828c2ecf20Sopenharmony_ci r->idiag_inode = sock_i_ino(sk); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci memset(&inet_sockopt, 0, sizeof(inet_sockopt)); 1858c2ecf20Sopenharmony_ci inet_sockopt.recverr = inet->recverr; 1868c2ecf20Sopenharmony_ci inet_sockopt.is_icsk = inet->is_icsk; 1878c2ecf20Sopenharmony_ci inet_sockopt.freebind = inet->freebind; 1888c2ecf20Sopenharmony_ci inet_sockopt.hdrincl = inet->hdrincl; 1898c2ecf20Sopenharmony_ci inet_sockopt.mc_loop = inet->mc_loop; 1908c2ecf20Sopenharmony_ci inet_sockopt.transparent = inet->transparent; 1918c2ecf20Sopenharmony_ci inet_sockopt.mc_all = inet->mc_all; 1928c2ecf20Sopenharmony_ci inet_sockopt.nodefrag = inet->nodefrag; 1938c2ecf20Sopenharmony_ci inet_sockopt.bind_address_no_port = inet->bind_address_no_port; 1948c2ecf20Sopenharmony_ci inet_sockopt.recverr_rfc4884 = inet->recverr_rfc4884; 1958c2ecf20Sopenharmony_ci inet_sockopt.defer_connect = inet->defer_connect; 1968c2ecf20Sopenharmony_ci if (nla_put(skb, INET_DIAG_SOCKOPT, sizeof(inet_sockopt), 1978c2ecf20Sopenharmony_ci &inet_sockopt)) 1988c2ecf20Sopenharmony_ci goto errout; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return 0; 2018c2ecf20Sopenharmony_cierrout: 2028c2ecf20Sopenharmony_ci return 1; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_diag_msg_attrs_fill); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic int inet_diag_parse_attrs(const struct nlmsghdr *nlh, int hdrlen, 2078c2ecf20Sopenharmony_ci struct nlattr **req_nlas) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct nlattr *nla; 2108c2ecf20Sopenharmony_ci int remaining; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci nlmsg_for_each_attr(nla, nlh, hdrlen, remaining) { 2138c2ecf20Sopenharmony_ci int type = nla_type(nla); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (type == INET_DIAG_REQ_PROTOCOL && nla_len(nla) != sizeof(u32)) 2168c2ecf20Sopenharmony_ci return -EINVAL; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (type < __INET_DIAG_REQ_MAX) 2198c2ecf20Sopenharmony_ci req_nlas[type] = nla; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int inet_diag_get_protocol(const struct inet_diag_req_v2 *req, 2258c2ecf20Sopenharmony_ci const struct inet_diag_dump_data *data) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci if (data->req_nlas[INET_DIAG_REQ_PROTOCOL]) 2288c2ecf20Sopenharmony_ci return nla_get_u32(data->req_nlas[INET_DIAG_REQ_PROTOCOL]); 2298c2ecf20Sopenharmony_ci return req->sdiag_protocol; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci#define MAX_DUMP_ALLOC_SIZE (KMALLOC_MAX_SIZE - SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ciint inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, 2358c2ecf20Sopenharmony_ci struct sk_buff *skb, struct netlink_callback *cb, 2368c2ecf20Sopenharmony_ci const struct inet_diag_req_v2 *req, 2378c2ecf20Sopenharmony_ci u16 nlmsg_flags, bool net_admin) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci const struct tcp_congestion_ops *ca_ops; 2408c2ecf20Sopenharmony_ci const struct inet_diag_handler *handler; 2418c2ecf20Sopenharmony_ci struct inet_diag_dump_data *cb_data; 2428c2ecf20Sopenharmony_ci int ext = req->idiag_ext; 2438c2ecf20Sopenharmony_ci struct inet_diag_msg *r; 2448c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 2458c2ecf20Sopenharmony_ci struct nlattr *attr; 2468c2ecf20Sopenharmony_ci void *info = NULL; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci cb_data = cb->data; 2498c2ecf20Sopenharmony_ci handler = inet_diag_table[inet_diag_get_protocol(req, cb_data)]; 2508c2ecf20Sopenharmony_ci BUG_ON(!handler); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 2538c2ecf20Sopenharmony_ci cb->nlh->nlmsg_type, sizeof(*r), nlmsg_flags); 2548c2ecf20Sopenharmony_ci if (!nlh) 2558c2ecf20Sopenharmony_ci return -EMSGSIZE; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci r = nlmsg_data(nlh); 2588c2ecf20Sopenharmony_ci BUG_ON(!sk_fullsock(sk)); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci inet_diag_msg_common_fill(r, sk); 2618c2ecf20Sopenharmony_ci r->idiag_state = sk->sk_state; 2628c2ecf20Sopenharmony_ci r->idiag_timer = 0; 2638c2ecf20Sopenharmony_ci r->idiag_retrans = 0; 2648c2ecf20Sopenharmony_ci r->idiag_expires = 0; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (inet_diag_msg_attrs_fill(sk, skb, r, ext, 2678c2ecf20Sopenharmony_ci sk_user_ns(NETLINK_CB(cb->skb).sk), 2688c2ecf20Sopenharmony_ci net_admin)) 2698c2ecf20Sopenharmony_ci goto errout; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (ext & (1 << (INET_DIAG_MEMINFO - 1))) { 2728c2ecf20Sopenharmony_ci struct inet_diag_meminfo minfo = { 2738c2ecf20Sopenharmony_ci .idiag_rmem = sk_rmem_alloc_get(sk), 2748c2ecf20Sopenharmony_ci .idiag_wmem = READ_ONCE(sk->sk_wmem_queued), 2758c2ecf20Sopenharmony_ci .idiag_fmem = sk->sk_forward_alloc, 2768c2ecf20Sopenharmony_ci .idiag_tmem = sk_wmem_alloc_get(sk), 2778c2ecf20Sopenharmony_ci }; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (nla_put(skb, INET_DIAG_MEMINFO, sizeof(minfo), &minfo) < 0) 2808c2ecf20Sopenharmony_ci goto errout; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (ext & (1 << (INET_DIAG_SKMEMINFO - 1))) 2848c2ecf20Sopenharmony_ci if (sock_diag_put_meminfo(sk, skb, INET_DIAG_SKMEMINFO)) 2858c2ecf20Sopenharmony_ci goto errout; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* 2888c2ecf20Sopenharmony_ci * RAW sockets might have user-defined protocols assigned, 2898c2ecf20Sopenharmony_ci * so report the one supplied on socket creation. 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_ci if (sk->sk_type == SOCK_RAW) { 2928c2ecf20Sopenharmony_ci if (nla_put_u8(skb, INET_DIAG_PROTOCOL, sk->sk_protocol)) 2938c2ecf20Sopenharmony_ci goto errout; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (!icsk) { 2978c2ecf20Sopenharmony_ci handler->idiag_get_info(sk, r, NULL); 2988c2ecf20Sopenharmony_ci goto out; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (icsk->icsk_pending == ICSK_TIME_RETRANS || 3028c2ecf20Sopenharmony_ci icsk->icsk_pending == ICSK_TIME_REO_TIMEOUT || 3038c2ecf20Sopenharmony_ci icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) { 3048c2ecf20Sopenharmony_ci r->idiag_timer = 1; 3058c2ecf20Sopenharmony_ci r->idiag_retrans = icsk->icsk_retransmits; 3068c2ecf20Sopenharmony_ci r->idiag_expires = 3078c2ecf20Sopenharmony_ci jiffies_delta_to_msecs(icsk->icsk_timeout - jiffies); 3088c2ecf20Sopenharmony_ci } else if (icsk->icsk_pending == ICSK_TIME_PROBE0) { 3098c2ecf20Sopenharmony_ci r->idiag_timer = 4; 3108c2ecf20Sopenharmony_ci r->idiag_retrans = icsk->icsk_probes_out; 3118c2ecf20Sopenharmony_ci r->idiag_expires = 3128c2ecf20Sopenharmony_ci jiffies_delta_to_msecs(icsk->icsk_timeout - jiffies); 3138c2ecf20Sopenharmony_ci } else if (timer_pending(&sk->sk_timer)) { 3148c2ecf20Sopenharmony_ci r->idiag_timer = 2; 3158c2ecf20Sopenharmony_ci r->idiag_retrans = icsk->icsk_probes_out; 3168c2ecf20Sopenharmony_ci r->idiag_expires = 3178c2ecf20Sopenharmony_ci jiffies_delta_to_msecs(sk->sk_timer.expires - jiffies); 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if ((ext & (1 << (INET_DIAG_INFO - 1))) && handler->idiag_info_size) { 3218c2ecf20Sopenharmony_ci attr = nla_reserve_64bit(skb, INET_DIAG_INFO, 3228c2ecf20Sopenharmony_ci handler->idiag_info_size, 3238c2ecf20Sopenharmony_ci INET_DIAG_PAD); 3248c2ecf20Sopenharmony_ci if (!attr) 3258c2ecf20Sopenharmony_ci goto errout; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci info = nla_data(attr); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci if (ext & (1 << (INET_DIAG_CONG - 1))) { 3318c2ecf20Sopenharmony_ci int err = 0; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci rcu_read_lock(); 3348c2ecf20Sopenharmony_ci ca_ops = READ_ONCE(icsk->icsk_ca_ops); 3358c2ecf20Sopenharmony_ci if (ca_ops) 3368c2ecf20Sopenharmony_ci err = nla_put_string(skb, INET_DIAG_CONG, ca_ops->name); 3378c2ecf20Sopenharmony_ci rcu_read_unlock(); 3388c2ecf20Sopenharmony_ci if (err < 0) 3398c2ecf20Sopenharmony_ci goto errout; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci handler->idiag_get_info(sk, r, info); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (ext & (1 << (INET_DIAG_INFO - 1)) && handler->idiag_get_aux) 3458c2ecf20Sopenharmony_ci if (handler->idiag_get_aux(sk, net_admin, skb) < 0) 3468c2ecf20Sopenharmony_ci goto errout; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (sk->sk_state < TCP_TIME_WAIT) { 3498c2ecf20Sopenharmony_ci union tcp_cc_info info; 3508c2ecf20Sopenharmony_ci size_t sz = 0; 3518c2ecf20Sopenharmony_ci int attr; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci rcu_read_lock(); 3548c2ecf20Sopenharmony_ci ca_ops = READ_ONCE(icsk->icsk_ca_ops); 3558c2ecf20Sopenharmony_ci if (ca_ops && ca_ops->get_info) 3568c2ecf20Sopenharmony_ci sz = ca_ops->get_info(sk, ext, &attr, &info); 3578c2ecf20Sopenharmony_ci rcu_read_unlock(); 3588c2ecf20Sopenharmony_ci if (sz && nla_put(skb, attr, sz, &info) < 0) 3598c2ecf20Sopenharmony_ci goto errout; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* Keep it at the end for potential retry with a larger skb, 3638c2ecf20Sopenharmony_ci * or else do best-effort fitting, which is only done for the 3648c2ecf20Sopenharmony_ci * first_nlmsg. 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_ci if (cb_data->bpf_stg_diag) { 3678c2ecf20Sopenharmony_ci bool first_nlmsg = ((unsigned char *)nlh == skb->data); 3688c2ecf20Sopenharmony_ci unsigned int prev_min_dump_alloc; 3698c2ecf20Sopenharmony_ci unsigned int total_nla_size = 0; 3708c2ecf20Sopenharmony_ci unsigned int msg_len; 3718c2ecf20Sopenharmony_ci int err; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci msg_len = skb_tail_pointer(skb) - (unsigned char *)nlh; 3748c2ecf20Sopenharmony_ci err = bpf_sk_storage_diag_put(cb_data->bpf_stg_diag, sk, skb, 3758c2ecf20Sopenharmony_ci INET_DIAG_SK_BPF_STORAGES, 3768c2ecf20Sopenharmony_ci &total_nla_size); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (!err) 3798c2ecf20Sopenharmony_ci goto out; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci total_nla_size += msg_len; 3828c2ecf20Sopenharmony_ci prev_min_dump_alloc = cb->min_dump_alloc; 3838c2ecf20Sopenharmony_ci if (total_nla_size > prev_min_dump_alloc) 3848c2ecf20Sopenharmony_ci cb->min_dump_alloc = min_t(u32, total_nla_size, 3858c2ecf20Sopenharmony_ci MAX_DUMP_ALLOC_SIZE); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (!first_nlmsg) 3888c2ecf20Sopenharmony_ci goto errout; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (cb->min_dump_alloc > prev_min_dump_alloc) 3918c2ecf20Sopenharmony_ci /* Retry with pskb_expand_head() with 3928c2ecf20Sopenharmony_ci * __GFP_DIRECT_RECLAIM 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_ci goto errout; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci WARN_ON_ONCE(total_nla_size <= prev_min_dump_alloc); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* Send what we have for this sk 3998c2ecf20Sopenharmony_ci * and move on to the next sk in the following 4008c2ecf20Sopenharmony_ci * dump() 4018c2ecf20Sopenharmony_ci */ 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ciout: 4058c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 4068c2ecf20Sopenharmony_ci return 0; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cierrout: 4098c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 4108c2ecf20Sopenharmony_ci return -EMSGSIZE; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_sk_diag_fill); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic int inet_twsk_diag_fill(struct sock *sk, 4158c2ecf20Sopenharmony_ci struct sk_buff *skb, 4168c2ecf20Sopenharmony_ci struct netlink_callback *cb, 4178c2ecf20Sopenharmony_ci u16 nlmsg_flags) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct inet_timewait_sock *tw = inet_twsk(sk); 4208c2ecf20Sopenharmony_ci struct inet_diag_msg *r; 4218c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 4228c2ecf20Sopenharmony_ci long tmo; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, 4258c2ecf20Sopenharmony_ci cb->nlh->nlmsg_seq, cb->nlh->nlmsg_type, 4268c2ecf20Sopenharmony_ci sizeof(*r), nlmsg_flags); 4278c2ecf20Sopenharmony_ci if (!nlh) 4288c2ecf20Sopenharmony_ci return -EMSGSIZE; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci r = nlmsg_data(nlh); 4318c2ecf20Sopenharmony_ci BUG_ON(tw->tw_state != TCP_TIME_WAIT); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci inet_diag_msg_common_fill(r, sk); 4348c2ecf20Sopenharmony_ci r->idiag_retrans = 0; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci r->idiag_state = tw->tw_substate; 4378c2ecf20Sopenharmony_ci r->idiag_timer = 3; 4388c2ecf20Sopenharmony_ci tmo = tw->tw_timer.expires - jiffies; 4398c2ecf20Sopenharmony_ci r->idiag_expires = jiffies_delta_to_msecs(tmo); 4408c2ecf20Sopenharmony_ci r->idiag_rqueue = 0; 4418c2ecf20Sopenharmony_ci r->idiag_wqueue = 0; 4428c2ecf20Sopenharmony_ci r->idiag_uid = 0; 4438c2ecf20Sopenharmony_ci r->idiag_inode = 0; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 4468c2ecf20Sopenharmony_ci return 0; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic int inet_req_diag_fill(struct sock *sk, struct sk_buff *skb, 4508c2ecf20Sopenharmony_ci struct netlink_callback *cb, 4518c2ecf20Sopenharmony_ci u16 nlmsg_flags, bool net_admin) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct request_sock *reqsk = inet_reqsk(sk); 4548c2ecf20Sopenharmony_ci struct inet_diag_msg *r; 4558c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 4568c2ecf20Sopenharmony_ci long tmo; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 4598c2ecf20Sopenharmony_ci cb->nlh->nlmsg_type, sizeof(*r), nlmsg_flags); 4608c2ecf20Sopenharmony_ci if (!nlh) 4618c2ecf20Sopenharmony_ci return -EMSGSIZE; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci r = nlmsg_data(nlh); 4648c2ecf20Sopenharmony_ci inet_diag_msg_common_fill(r, sk); 4658c2ecf20Sopenharmony_ci r->idiag_state = TCP_SYN_RECV; 4668c2ecf20Sopenharmony_ci r->idiag_timer = 1; 4678c2ecf20Sopenharmony_ci r->idiag_retrans = reqsk->num_retrans; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci BUILD_BUG_ON(offsetof(struct inet_request_sock, ir_cookie) != 4708c2ecf20Sopenharmony_ci offsetof(struct sock, sk_cookie)); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci tmo = inet_reqsk(sk)->rsk_timer.expires - jiffies; 4738c2ecf20Sopenharmony_ci r->idiag_expires = jiffies_delta_to_msecs(tmo); 4748c2ecf20Sopenharmony_ci r->idiag_rqueue = 0; 4758c2ecf20Sopenharmony_ci r->idiag_wqueue = 0; 4768c2ecf20Sopenharmony_ci r->idiag_uid = 0; 4778c2ecf20Sopenharmony_ci r->idiag_inode = 0; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (net_admin && nla_put_u32(skb, INET_DIAG_MARK, 4808c2ecf20Sopenharmony_ci inet_rsk(reqsk)->ir_mark)) { 4818c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 4828c2ecf20Sopenharmony_ci return -EMSGSIZE; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 4868c2ecf20Sopenharmony_ci return 0; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic int sk_diag_fill(struct sock *sk, struct sk_buff *skb, 4908c2ecf20Sopenharmony_ci struct netlink_callback *cb, 4918c2ecf20Sopenharmony_ci const struct inet_diag_req_v2 *r, 4928c2ecf20Sopenharmony_ci u16 nlmsg_flags, bool net_admin) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci if (sk->sk_state == TCP_TIME_WAIT) 4958c2ecf20Sopenharmony_ci return inet_twsk_diag_fill(sk, skb, cb, nlmsg_flags); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (sk->sk_state == TCP_NEW_SYN_RECV) 4988c2ecf20Sopenharmony_ci return inet_req_diag_fill(sk, skb, cb, nlmsg_flags, net_admin); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci return inet_sk_diag_fill(sk, inet_csk(sk), skb, cb, r, nlmsg_flags, 5018c2ecf20Sopenharmony_ci net_admin); 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistruct sock *inet_diag_find_one_icsk(struct net *net, 5058c2ecf20Sopenharmony_ci struct inet_hashinfo *hashinfo, 5068c2ecf20Sopenharmony_ci const struct inet_diag_req_v2 *req) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci struct sock *sk; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci rcu_read_lock(); 5118c2ecf20Sopenharmony_ci if (req->sdiag_family == AF_INET) 5128c2ecf20Sopenharmony_ci sk = inet_lookup(net, hashinfo, NULL, 0, req->id.idiag_dst[0], 5138c2ecf20Sopenharmony_ci req->id.idiag_dport, req->id.idiag_src[0], 5148c2ecf20Sopenharmony_ci req->id.idiag_sport, req->id.idiag_if); 5158c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 5168c2ecf20Sopenharmony_ci else if (req->sdiag_family == AF_INET6) { 5178c2ecf20Sopenharmony_ci if (ipv6_addr_v4mapped((struct in6_addr *)req->id.idiag_dst) && 5188c2ecf20Sopenharmony_ci ipv6_addr_v4mapped((struct in6_addr *)req->id.idiag_src)) 5198c2ecf20Sopenharmony_ci sk = inet_lookup(net, hashinfo, NULL, 0, req->id.idiag_dst[3], 5208c2ecf20Sopenharmony_ci req->id.idiag_dport, req->id.idiag_src[3], 5218c2ecf20Sopenharmony_ci req->id.idiag_sport, req->id.idiag_if); 5228c2ecf20Sopenharmony_ci else 5238c2ecf20Sopenharmony_ci sk = inet6_lookup(net, hashinfo, NULL, 0, 5248c2ecf20Sopenharmony_ci (struct in6_addr *)req->id.idiag_dst, 5258c2ecf20Sopenharmony_ci req->id.idiag_dport, 5268c2ecf20Sopenharmony_ci (struct in6_addr *)req->id.idiag_src, 5278c2ecf20Sopenharmony_ci req->id.idiag_sport, 5288c2ecf20Sopenharmony_ci req->id.idiag_if); 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci#endif 5318c2ecf20Sopenharmony_ci else { 5328c2ecf20Sopenharmony_ci rcu_read_unlock(); 5338c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci rcu_read_unlock(); 5368c2ecf20Sopenharmony_ci if (!sk) 5378c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if (sock_diag_check_cookie(sk, req->id.idiag_cookie)) { 5408c2ecf20Sopenharmony_ci sock_gen_put(sk); 5418c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci return sk; 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_diag_find_one_icsk); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ciint inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, 5498c2ecf20Sopenharmony_ci struct netlink_callback *cb, 5508c2ecf20Sopenharmony_ci const struct inet_diag_req_v2 *req) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct sk_buff *in_skb = cb->skb; 5538c2ecf20Sopenharmony_ci bool net_admin = netlink_net_capable(in_skb, CAP_NET_ADMIN); 5548c2ecf20Sopenharmony_ci struct net *net = sock_net(in_skb->sk); 5558c2ecf20Sopenharmony_ci struct sk_buff *rep; 5568c2ecf20Sopenharmony_ci struct sock *sk; 5578c2ecf20Sopenharmony_ci int err; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci sk = inet_diag_find_one_icsk(net, hashinfo, req); 5608c2ecf20Sopenharmony_ci if (IS_ERR(sk)) 5618c2ecf20Sopenharmony_ci return PTR_ERR(sk); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci rep = nlmsg_new(inet_sk_attr_size(sk, req, net_admin), GFP_KERNEL); 5648c2ecf20Sopenharmony_ci if (!rep) { 5658c2ecf20Sopenharmony_ci err = -ENOMEM; 5668c2ecf20Sopenharmony_ci goto out; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci err = sk_diag_fill(sk, rep, cb, req, 0, net_admin); 5708c2ecf20Sopenharmony_ci if (err < 0) { 5718c2ecf20Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 5728c2ecf20Sopenharmony_ci nlmsg_free(rep); 5738c2ecf20Sopenharmony_ci goto out; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid, 5768c2ecf20Sopenharmony_ci MSG_DONTWAIT); 5778c2ecf20Sopenharmony_ci if (err > 0) 5788c2ecf20Sopenharmony_ci err = 0; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ciout: 5818c2ecf20Sopenharmony_ci if (sk) 5828c2ecf20Sopenharmony_ci sock_gen_put(sk); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci return err; 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_diag_dump_one_icsk); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic int inet_diag_cmd_exact(int cmd, struct sk_buff *in_skb, 5898c2ecf20Sopenharmony_ci const struct nlmsghdr *nlh, 5908c2ecf20Sopenharmony_ci int hdrlen, 5918c2ecf20Sopenharmony_ci const struct inet_diag_req_v2 *req) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci const struct inet_diag_handler *handler; 5948c2ecf20Sopenharmony_ci struct inet_diag_dump_data dump_data; 5958c2ecf20Sopenharmony_ci int err, protocol; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci memset(&dump_data, 0, sizeof(dump_data)); 5988c2ecf20Sopenharmony_ci err = inet_diag_parse_attrs(nlh, hdrlen, dump_data.req_nlas); 5998c2ecf20Sopenharmony_ci if (err) 6008c2ecf20Sopenharmony_ci return err; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci protocol = inet_diag_get_protocol(req, &dump_data); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci handler = inet_diag_lock_handler(protocol); 6058c2ecf20Sopenharmony_ci if (IS_ERR(handler)) { 6068c2ecf20Sopenharmony_ci err = PTR_ERR(handler); 6078c2ecf20Sopenharmony_ci } else if (cmd == SOCK_DIAG_BY_FAMILY) { 6088c2ecf20Sopenharmony_ci struct netlink_callback cb = { 6098c2ecf20Sopenharmony_ci .nlh = nlh, 6108c2ecf20Sopenharmony_ci .skb = in_skb, 6118c2ecf20Sopenharmony_ci .data = &dump_data, 6128c2ecf20Sopenharmony_ci }; 6138c2ecf20Sopenharmony_ci err = handler->dump_one(&cb, req); 6148c2ecf20Sopenharmony_ci } else if (cmd == SOCK_DESTROY && handler->destroy) { 6158c2ecf20Sopenharmony_ci err = handler->destroy(in_skb, req); 6168c2ecf20Sopenharmony_ci } else { 6178c2ecf20Sopenharmony_ci err = -EOPNOTSUPP; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci inet_diag_unlock_handler(handler); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci return err; 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_cistatic int bitstring_match(const __be32 *a1, const __be32 *a2, int bits) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci int words = bits >> 5; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci bits &= 0x1f; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (words) { 6318c2ecf20Sopenharmony_ci if (memcmp(a1, a2, words << 2)) 6328c2ecf20Sopenharmony_ci return 0; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci if (bits) { 6358c2ecf20Sopenharmony_ci __be32 w1, w2; 6368c2ecf20Sopenharmony_ci __be32 mask; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci w1 = a1[words]; 6398c2ecf20Sopenharmony_ci w2 = a2[words]; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci mask = htonl((0xffffffff) << (32 - bits)); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if ((w1 ^ w2) & mask) 6448c2ecf20Sopenharmony_ci return 0; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci return 1; 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_cistatic int inet_diag_bc_run(const struct nlattr *_bc, 6518c2ecf20Sopenharmony_ci const struct inet_diag_entry *entry) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci const void *bc = nla_data(_bc); 6548c2ecf20Sopenharmony_ci int len = nla_len(_bc); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci while (len > 0) { 6578c2ecf20Sopenharmony_ci int yes = 1; 6588c2ecf20Sopenharmony_ci const struct inet_diag_bc_op *op = bc; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci switch (op->code) { 6618c2ecf20Sopenharmony_ci case INET_DIAG_BC_NOP: 6628c2ecf20Sopenharmony_ci break; 6638c2ecf20Sopenharmony_ci case INET_DIAG_BC_JMP: 6648c2ecf20Sopenharmony_ci yes = 0; 6658c2ecf20Sopenharmony_ci break; 6668c2ecf20Sopenharmony_ci case INET_DIAG_BC_S_EQ: 6678c2ecf20Sopenharmony_ci yes = entry->sport == op[1].no; 6688c2ecf20Sopenharmony_ci break; 6698c2ecf20Sopenharmony_ci case INET_DIAG_BC_S_GE: 6708c2ecf20Sopenharmony_ci yes = entry->sport >= op[1].no; 6718c2ecf20Sopenharmony_ci break; 6728c2ecf20Sopenharmony_ci case INET_DIAG_BC_S_LE: 6738c2ecf20Sopenharmony_ci yes = entry->sport <= op[1].no; 6748c2ecf20Sopenharmony_ci break; 6758c2ecf20Sopenharmony_ci case INET_DIAG_BC_D_EQ: 6768c2ecf20Sopenharmony_ci yes = entry->dport == op[1].no; 6778c2ecf20Sopenharmony_ci break; 6788c2ecf20Sopenharmony_ci case INET_DIAG_BC_D_GE: 6798c2ecf20Sopenharmony_ci yes = entry->dport >= op[1].no; 6808c2ecf20Sopenharmony_ci break; 6818c2ecf20Sopenharmony_ci case INET_DIAG_BC_D_LE: 6828c2ecf20Sopenharmony_ci yes = entry->dport <= op[1].no; 6838c2ecf20Sopenharmony_ci break; 6848c2ecf20Sopenharmony_ci case INET_DIAG_BC_AUTO: 6858c2ecf20Sopenharmony_ci yes = !(entry->userlocks & SOCK_BINDPORT_LOCK); 6868c2ecf20Sopenharmony_ci break; 6878c2ecf20Sopenharmony_ci case INET_DIAG_BC_S_COND: 6888c2ecf20Sopenharmony_ci case INET_DIAG_BC_D_COND: { 6898c2ecf20Sopenharmony_ci const struct inet_diag_hostcond *cond; 6908c2ecf20Sopenharmony_ci const __be32 *addr; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci cond = (const struct inet_diag_hostcond *)(op + 1); 6938c2ecf20Sopenharmony_ci if (cond->port != -1 && 6948c2ecf20Sopenharmony_ci cond->port != (op->code == INET_DIAG_BC_S_COND ? 6958c2ecf20Sopenharmony_ci entry->sport : entry->dport)) { 6968c2ecf20Sopenharmony_ci yes = 0; 6978c2ecf20Sopenharmony_ci break; 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (op->code == INET_DIAG_BC_S_COND) 7018c2ecf20Sopenharmony_ci addr = entry->saddr; 7028c2ecf20Sopenharmony_ci else 7038c2ecf20Sopenharmony_ci addr = entry->daddr; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci if (cond->family != AF_UNSPEC && 7068c2ecf20Sopenharmony_ci cond->family != entry->family) { 7078c2ecf20Sopenharmony_ci if (entry->family == AF_INET6 && 7088c2ecf20Sopenharmony_ci cond->family == AF_INET) { 7098c2ecf20Sopenharmony_ci if (addr[0] == 0 && addr[1] == 0 && 7108c2ecf20Sopenharmony_ci addr[2] == htonl(0xffff) && 7118c2ecf20Sopenharmony_ci bitstring_match(addr + 3, 7128c2ecf20Sopenharmony_ci cond->addr, 7138c2ecf20Sopenharmony_ci cond->prefix_len)) 7148c2ecf20Sopenharmony_ci break; 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci yes = 0; 7178c2ecf20Sopenharmony_ci break; 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (cond->prefix_len == 0) 7218c2ecf20Sopenharmony_ci break; 7228c2ecf20Sopenharmony_ci if (bitstring_match(addr, cond->addr, 7238c2ecf20Sopenharmony_ci cond->prefix_len)) 7248c2ecf20Sopenharmony_ci break; 7258c2ecf20Sopenharmony_ci yes = 0; 7268c2ecf20Sopenharmony_ci break; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci case INET_DIAG_BC_DEV_COND: { 7298c2ecf20Sopenharmony_ci u32 ifindex; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci ifindex = *((const u32 *)(op + 1)); 7328c2ecf20Sopenharmony_ci if (ifindex != entry->ifindex) 7338c2ecf20Sopenharmony_ci yes = 0; 7348c2ecf20Sopenharmony_ci break; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci case INET_DIAG_BC_MARK_COND: { 7378c2ecf20Sopenharmony_ci struct inet_diag_markcond *cond; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci cond = (struct inet_diag_markcond *)(op + 1); 7408c2ecf20Sopenharmony_ci if ((entry->mark & cond->mask) != cond->mark) 7418c2ecf20Sopenharmony_ci yes = 0; 7428c2ecf20Sopenharmony_ci break; 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci#ifdef CONFIG_SOCK_CGROUP_DATA 7458c2ecf20Sopenharmony_ci case INET_DIAG_BC_CGROUP_COND: { 7468c2ecf20Sopenharmony_ci u64 cgroup_id; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci cgroup_id = get_unaligned((const u64 *)(op + 1)); 7498c2ecf20Sopenharmony_ci if (cgroup_id != entry->cgroup_id) 7508c2ecf20Sopenharmony_ci yes = 0; 7518c2ecf20Sopenharmony_ci break; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci#endif 7548c2ecf20Sopenharmony_ci } 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci if (yes) { 7578c2ecf20Sopenharmony_ci len -= op->yes; 7588c2ecf20Sopenharmony_ci bc += op->yes; 7598c2ecf20Sopenharmony_ci } else { 7608c2ecf20Sopenharmony_ci len -= op->no; 7618c2ecf20Sopenharmony_ci bc += op->no; 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci return len == 0; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci/* This helper is available for all sockets (ESTABLISH, TIMEWAIT, SYN_RECV) 7688c2ecf20Sopenharmony_ci */ 7698c2ecf20Sopenharmony_cistatic void entry_fill_addrs(struct inet_diag_entry *entry, 7708c2ecf20Sopenharmony_ci const struct sock *sk) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 7738c2ecf20Sopenharmony_ci if (sk->sk_family == AF_INET6) { 7748c2ecf20Sopenharmony_ci entry->saddr = sk->sk_v6_rcv_saddr.s6_addr32; 7758c2ecf20Sopenharmony_ci entry->daddr = sk->sk_v6_daddr.s6_addr32; 7768c2ecf20Sopenharmony_ci } else 7778c2ecf20Sopenharmony_ci#endif 7788c2ecf20Sopenharmony_ci { 7798c2ecf20Sopenharmony_ci entry->saddr = &sk->sk_rcv_saddr; 7808c2ecf20Sopenharmony_ci entry->daddr = &sk->sk_daddr; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci} 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ciint inet_diag_bc_sk(const struct nlattr *bc, struct sock *sk) 7858c2ecf20Sopenharmony_ci{ 7868c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 7878c2ecf20Sopenharmony_ci struct inet_diag_entry entry; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci if (!bc) 7908c2ecf20Sopenharmony_ci return 1; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci entry.family = sk->sk_family; 7938c2ecf20Sopenharmony_ci entry_fill_addrs(&entry, sk); 7948c2ecf20Sopenharmony_ci entry.sport = inet->inet_num; 7958c2ecf20Sopenharmony_ci entry.dport = ntohs(inet->inet_dport); 7968c2ecf20Sopenharmony_ci entry.ifindex = sk->sk_bound_dev_if; 7978c2ecf20Sopenharmony_ci entry.userlocks = sk_fullsock(sk) ? sk->sk_userlocks : 0; 7988c2ecf20Sopenharmony_ci if (sk_fullsock(sk)) 7998c2ecf20Sopenharmony_ci entry.mark = sk->sk_mark; 8008c2ecf20Sopenharmony_ci else if (sk->sk_state == TCP_NEW_SYN_RECV) 8018c2ecf20Sopenharmony_ci entry.mark = inet_rsk(inet_reqsk(sk))->ir_mark; 8028c2ecf20Sopenharmony_ci else 8038c2ecf20Sopenharmony_ci entry.mark = 0; 8048c2ecf20Sopenharmony_ci#ifdef CONFIG_SOCK_CGROUP_DATA 8058c2ecf20Sopenharmony_ci entry.cgroup_id = sk_fullsock(sk) ? 8068c2ecf20Sopenharmony_ci cgroup_id(sock_cgroup_ptr(&sk->sk_cgrp_data)) : 0; 8078c2ecf20Sopenharmony_ci#endif 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci return inet_diag_bc_run(bc, &entry); 8108c2ecf20Sopenharmony_ci} 8118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_diag_bc_sk); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cistatic int valid_cc(const void *bc, int len, int cc) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci while (len >= 0) { 8168c2ecf20Sopenharmony_ci const struct inet_diag_bc_op *op = bc; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci if (cc > len) 8198c2ecf20Sopenharmony_ci return 0; 8208c2ecf20Sopenharmony_ci if (cc == len) 8218c2ecf20Sopenharmony_ci return 1; 8228c2ecf20Sopenharmony_ci if (op->yes < 4 || op->yes & 3) 8238c2ecf20Sopenharmony_ci return 0; 8248c2ecf20Sopenharmony_ci len -= op->yes; 8258c2ecf20Sopenharmony_ci bc += op->yes; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci return 0; 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci/* data is u32 ifindex */ 8318c2ecf20Sopenharmony_cistatic bool valid_devcond(const struct inet_diag_bc_op *op, int len, 8328c2ecf20Sopenharmony_ci int *min_len) 8338c2ecf20Sopenharmony_ci{ 8348c2ecf20Sopenharmony_ci /* Check ifindex space. */ 8358c2ecf20Sopenharmony_ci *min_len += sizeof(u32); 8368c2ecf20Sopenharmony_ci if (len < *min_len) 8378c2ecf20Sopenharmony_ci return false; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci return true; 8408c2ecf20Sopenharmony_ci} 8418c2ecf20Sopenharmony_ci/* Validate an inet_diag_hostcond. */ 8428c2ecf20Sopenharmony_cistatic bool valid_hostcond(const struct inet_diag_bc_op *op, int len, 8438c2ecf20Sopenharmony_ci int *min_len) 8448c2ecf20Sopenharmony_ci{ 8458c2ecf20Sopenharmony_ci struct inet_diag_hostcond *cond; 8468c2ecf20Sopenharmony_ci int addr_len; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci /* Check hostcond space. */ 8498c2ecf20Sopenharmony_ci *min_len += sizeof(struct inet_diag_hostcond); 8508c2ecf20Sopenharmony_ci if (len < *min_len) 8518c2ecf20Sopenharmony_ci return false; 8528c2ecf20Sopenharmony_ci cond = (struct inet_diag_hostcond *)(op + 1); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci /* Check address family and address length. */ 8558c2ecf20Sopenharmony_ci switch (cond->family) { 8568c2ecf20Sopenharmony_ci case AF_UNSPEC: 8578c2ecf20Sopenharmony_ci addr_len = 0; 8588c2ecf20Sopenharmony_ci break; 8598c2ecf20Sopenharmony_ci case AF_INET: 8608c2ecf20Sopenharmony_ci addr_len = sizeof(struct in_addr); 8618c2ecf20Sopenharmony_ci break; 8628c2ecf20Sopenharmony_ci case AF_INET6: 8638c2ecf20Sopenharmony_ci addr_len = sizeof(struct in6_addr); 8648c2ecf20Sopenharmony_ci break; 8658c2ecf20Sopenharmony_ci default: 8668c2ecf20Sopenharmony_ci return false; 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci *min_len += addr_len; 8698c2ecf20Sopenharmony_ci if (len < *min_len) 8708c2ecf20Sopenharmony_ci return false; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci /* Check prefix length (in bits) vs address length (in bytes). */ 8738c2ecf20Sopenharmony_ci if (cond->prefix_len > 8 * addr_len) 8748c2ecf20Sopenharmony_ci return false; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci return true; 8778c2ecf20Sopenharmony_ci} 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci/* Validate a port comparison operator. */ 8808c2ecf20Sopenharmony_cistatic bool valid_port_comparison(const struct inet_diag_bc_op *op, 8818c2ecf20Sopenharmony_ci int len, int *min_len) 8828c2ecf20Sopenharmony_ci{ 8838c2ecf20Sopenharmony_ci /* Port comparisons put the port in a follow-on inet_diag_bc_op. */ 8848c2ecf20Sopenharmony_ci *min_len += sizeof(struct inet_diag_bc_op); 8858c2ecf20Sopenharmony_ci if (len < *min_len) 8868c2ecf20Sopenharmony_ci return false; 8878c2ecf20Sopenharmony_ci return true; 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic bool valid_markcond(const struct inet_diag_bc_op *op, int len, 8918c2ecf20Sopenharmony_ci int *min_len) 8928c2ecf20Sopenharmony_ci{ 8938c2ecf20Sopenharmony_ci *min_len += sizeof(struct inet_diag_markcond); 8948c2ecf20Sopenharmony_ci return len >= *min_len; 8958c2ecf20Sopenharmony_ci} 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci#ifdef CONFIG_SOCK_CGROUP_DATA 8988c2ecf20Sopenharmony_cistatic bool valid_cgroupcond(const struct inet_diag_bc_op *op, int len, 8998c2ecf20Sopenharmony_ci int *min_len) 9008c2ecf20Sopenharmony_ci{ 9018c2ecf20Sopenharmony_ci *min_len += sizeof(u64); 9028c2ecf20Sopenharmony_ci return len >= *min_len; 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ci#endif 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_cistatic int inet_diag_bc_audit(const struct nlattr *attr, 9078c2ecf20Sopenharmony_ci const struct sk_buff *skb) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci bool net_admin = netlink_net_capable(skb, CAP_NET_ADMIN); 9108c2ecf20Sopenharmony_ci const void *bytecode, *bc; 9118c2ecf20Sopenharmony_ci int bytecode_len, len; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci if (!attr || nla_len(attr) < sizeof(struct inet_diag_bc_op)) 9148c2ecf20Sopenharmony_ci return -EINVAL; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci bytecode = bc = nla_data(attr); 9178c2ecf20Sopenharmony_ci len = bytecode_len = nla_len(attr); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci while (len > 0) { 9208c2ecf20Sopenharmony_ci int min_len = sizeof(struct inet_diag_bc_op); 9218c2ecf20Sopenharmony_ci const struct inet_diag_bc_op *op = bc; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci switch (op->code) { 9248c2ecf20Sopenharmony_ci case INET_DIAG_BC_S_COND: 9258c2ecf20Sopenharmony_ci case INET_DIAG_BC_D_COND: 9268c2ecf20Sopenharmony_ci if (!valid_hostcond(bc, len, &min_len)) 9278c2ecf20Sopenharmony_ci return -EINVAL; 9288c2ecf20Sopenharmony_ci break; 9298c2ecf20Sopenharmony_ci case INET_DIAG_BC_DEV_COND: 9308c2ecf20Sopenharmony_ci if (!valid_devcond(bc, len, &min_len)) 9318c2ecf20Sopenharmony_ci return -EINVAL; 9328c2ecf20Sopenharmony_ci break; 9338c2ecf20Sopenharmony_ci case INET_DIAG_BC_S_EQ: 9348c2ecf20Sopenharmony_ci case INET_DIAG_BC_S_GE: 9358c2ecf20Sopenharmony_ci case INET_DIAG_BC_S_LE: 9368c2ecf20Sopenharmony_ci case INET_DIAG_BC_D_EQ: 9378c2ecf20Sopenharmony_ci case INET_DIAG_BC_D_GE: 9388c2ecf20Sopenharmony_ci case INET_DIAG_BC_D_LE: 9398c2ecf20Sopenharmony_ci if (!valid_port_comparison(bc, len, &min_len)) 9408c2ecf20Sopenharmony_ci return -EINVAL; 9418c2ecf20Sopenharmony_ci break; 9428c2ecf20Sopenharmony_ci case INET_DIAG_BC_MARK_COND: 9438c2ecf20Sopenharmony_ci if (!net_admin) 9448c2ecf20Sopenharmony_ci return -EPERM; 9458c2ecf20Sopenharmony_ci if (!valid_markcond(bc, len, &min_len)) 9468c2ecf20Sopenharmony_ci return -EINVAL; 9478c2ecf20Sopenharmony_ci break; 9488c2ecf20Sopenharmony_ci#ifdef CONFIG_SOCK_CGROUP_DATA 9498c2ecf20Sopenharmony_ci case INET_DIAG_BC_CGROUP_COND: 9508c2ecf20Sopenharmony_ci if (!valid_cgroupcond(bc, len, &min_len)) 9518c2ecf20Sopenharmony_ci return -EINVAL; 9528c2ecf20Sopenharmony_ci break; 9538c2ecf20Sopenharmony_ci#endif 9548c2ecf20Sopenharmony_ci case INET_DIAG_BC_AUTO: 9558c2ecf20Sopenharmony_ci case INET_DIAG_BC_JMP: 9568c2ecf20Sopenharmony_ci case INET_DIAG_BC_NOP: 9578c2ecf20Sopenharmony_ci break; 9588c2ecf20Sopenharmony_ci default: 9598c2ecf20Sopenharmony_ci return -EINVAL; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci if (op->code != INET_DIAG_BC_NOP) { 9638c2ecf20Sopenharmony_ci if (op->no < min_len || op->no > len + 4 || op->no & 3) 9648c2ecf20Sopenharmony_ci return -EINVAL; 9658c2ecf20Sopenharmony_ci if (op->no < len && 9668c2ecf20Sopenharmony_ci !valid_cc(bytecode, bytecode_len, len - op->no)) 9678c2ecf20Sopenharmony_ci return -EINVAL; 9688c2ecf20Sopenharmony_ci } 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci if (op->yes < min_len || op->yes > len + 4 || op->yes & 3) 9718c2ecf20Sopenharmony_ci return -EINVAL; 9728c2ecf20Sopenharmony_ci bc += op->yes; 9738c2ecf20Sopenharmony_ci len -= op->yes; 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci return len == 0 ? 0 : -EINVAL; 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_cistatic void twsk_build_assert(void) 9798c2ecf20Sopenharmony_ci{ 9808c2ecf20Sopenharmony_ci BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_family) != 9818c2ecf20Sopenharmony_ci offsetof(struct sock, sk_family)); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_num) != 9848c2ecf20Sopenharmony_ci offsetof(struct inet_sock, inet_num)); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_dport) != 9878c2ecf20Sopenharmony_ci offsetof(struct inet_sock, inet_dport)); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_rcv_saddr) != 9908c2ecf20Sopenharmony_ci offsetof(struct inet_sock, inet_rcv_saddr)); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_daddr) != 9938c2ecf20Sopenharmony_ci offsetof(struct inet_sock, inet_daddr)); 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 9968c2ecf20Sopenharmony_ci BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_v6_rcv_saddr) != 9978c2ecf20Sopenharmony_ci offsetof(struct sock, sk_v6_rcv_saddr)); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_v6_daddr) != 10008c2ecf20Sopenharmony_ci offsetof(struct sock, sk_v6_daddr)); 10018c2ecf20Sopenharmony_ci#endif 10028c2ecf20Sopenharmony_ci} 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_civoid inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, 10058c2ecf20Sopenharmony_ci struct netlink_callback *cb, 10068c2ecf20Sopenharmony_ci const struct inet_diag_req_v2 *r) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci bool net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN); 10098c2ecf20Sopenharmony_ci struct inet_diag_dump_data *cb_data = cb->data; 10108c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 10118c2ecf20Sopenharmony_ci u32 idiag_states = r->idiag_states; 10128c2ecf20Sopenharmony_ci int i, num, s_i, s_num; 10138c2ecf20Sopenharmony_ci struct nlattr *bc; 10148c2ecf20Sopenharmony_ci struct sock *sk; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci bc = cb_data->inet_diag_nla_bc; 10178c2ecf20Sopenharmony_ci if (idiag_states & TCPF_SYN_RECV) 10188c2ecf20Sopenharmony_ci idiag_states |= TCPF_NEW_SYN_RECV; 10198c2ecf20Sopenharmony_ci s_i = cb->args[1]; 10208c2ecf20Sopenharmony_ci s_num = num = cb->args[2]; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci if (cb->args[0] == 0) { 10238c2ecf20Sopenharmony_ci if (!(idiag_states & TCPF_LISTEN) || r->id.idiag_dport) 10248c2ecf20Sopenharmony_ci goto skip_listen_ht; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci for (i = s_i; i < INET_LHTABLE_SIZE; i++) { 10278c2ecf20Sopenharmony_ci struct inet_listen_hashbucket *ilb; 10288c2ecf20Sopenharmony_ci struct hlist_nulls_node *node; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci num = 0; 10318c2ecf20Sopenharmony_ci ilb = &hashinfo->listening_hash[i]; 10328c2ecf20Sopenharmony_ci spin_lock(&ilb->lock); 10338c2ecf20Sopenharmony_ci sk_nulls_for_each(sk, node, &ilb->nulls_head) { 10348c2ecf20Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci if (!net_eq(sock_net(sk), net)) 10378c2ecf20Sopenharmony_ci continue; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci if (num < s_num) { 10408c2ecf20Sopenharmony_ci num++; 10418c2ecf20Sopenharmony_ci continue; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci if (r->sdiag_family != AF_UNSPEC && 10458c2ecf20Sopenharmony_ci sk->sk_family != r->sdiag_family) 10468c2ecf20Sopenharmony_ci goto next_listen; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci if (r->id.idiag_sport != inet->inet_sport && 10498c2ecf20Sopenharmony_ci r->id.idiag_sport) 10508c2ecf20Sopenharmony_ci goto next_listen; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if (!inet_diag_bc_sk(bc, sk)) 10538c2ecf20Sopenharmony_ci goto next_listen; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci if (inet_sk_diag_fill(sk, inet_csk(sk), skb, 10568c2ecf20Sopenharmony_ci cb, r, NLM_F_MULTI, 10578c2ecf20Sopenharmony_ci net_admin) < 0) { 10588c2ecf20Sopenharmony_ci spin_unlock(&ilb->lock); 10598c2ecf20Sopenharmony_ci goto done; 10608c2ecf20Sopenharmony_ci } 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_cinext_listen: 10638c2ecf20Sopenharmony_ci ++num; 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci spin_unlock(&ilb->lock); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci s_num = 0; 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ciskip_listen_ht: 10708c2ecf20Sopenharmony_ci cb->args[0] = 1; 10718c2ecf20Sopenharmony_ci s_i = num = s_num = 0; 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci if (!(idiag_states & ~TCPF_LISTEN)) 10758c2ecf20Sopenharmony_ci goto out; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci#define SKARR_SZ 16 10788c2ecf20Sopenharmony_ci for (i = s_i; i <= hashinfo->ehash_mask; i++) { 10798c2ecf20Sopenharmony_ci struct inet_ehash_bucket *head = &hashinfo->ehash[i]; 10808c2ecf20Sopenharmony_ci spinlock_t *lock = inet_ehash_lockp(hashinfo, i); 10818c2ecf20Sopenharmony_ci struct hlist_nulls_node *node; 10828c2ecf20Sopenharmony_ci struct sock *sk_arr[SKARR_SZ]; 10838c2ecf20Sopenharmony_ci int num_arr[SKARR_SZ]; 10848c2ecf20Sopenharmony_ci int idx, accum, res; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci if (hlist_nulls_empty(&head->chain)) 10878c2ecf20Sopenharmony_ci continue; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci if (i > s_i) 10908c2ecf20Sopenharmony_ci s_num = 0; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_cinext_chunk: 10938c2ecf20Sopenharmony_ci num = 0; 10948c2ecf20Sopenharmony_ci accum = 0; 10958c2ecf20Sopenharmony_ci spin_lock_bh(lock); 10968c2ecf20Sopenharmony_ci sk_nulls_for_each(sk, node, &head->chain) { 10978c2ecf20Sopenharmony_ci int state; 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci if (!net_eq(sock_net(sk), net)) 11008c2ecf20Sopenharmony_ci continue; 11018c2ecf20Sopenharmony_ci if (num < s_num) 11028c2ecf20Sopenharmony_ci goto next_normal; 11038c2ecf20Sopenharmony_ci state = (sk->sk_state == TCP_TIME_WAIT) ? 11048c2ecf20Sopenharmony_ci inet_twsk(sk)->tw_substate : sk->sk_state; 11058c2ecf20Sopenharmony_ci if (!(idiag_states & (1 << state))) 11068c2ecf20Sopenharmony_ci goto next_normal; 11078c2ecf20Sopenharmony_ci if (r->sdiag_family != AF_UNSPEC && 11088c2ecf20Sopenharmony_ci sk->sk_family != r->sdiag_family) 11098c2ecf20Sopenharmony_ci goto next_normal; 11108c2ecf20Sopenharmony_ci if (r->id.idiag_sport != htons(sk->sk_num) && 11118c2ecf20Sopenharmony_ci r->id.idiag_sport) 11128c2ecf20Sopenharmony_ci goto next_normal; 11138c2ecf20Sopenharmony_ci if (r->id.idiag_dport != sk->sk_dport && 11148c2ecf20Sopenharmony_ci r->id.idiag_dport) 11158c2ecf20Sopenharmony_ci goto next_normal; 11168c2ecf20Sopenharmony_ci twsk_build_assert(); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci if (!inet_diag_bc_sk(bc, sk)) 11198c2ecf20Sopenharmony_ci goto next_normal; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci if (!refcount_inc_not_zero(&sk->sk_refcnt)) 11228c2ecf20Sopenharmony_ci goto next_normal; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci num_arr[accum] = num; 11258c2ecf20Sopenharmony_ci sk_arr[accum] = sk; 11268c2ecf20Sopenharmony_ci if (++accum == SKARR_SZ) 11278c2ecf20Sopenharmony_ci break; 11288c2ecf20Sopenharmony_cinext_normal: 11298c2ecf20Sopenharmony_ci ++num; 11308c2ecf20Sopenharmony_ci } 11318c2ecf20Sopenharmony_ci spin_unlock_bh(lock); 11328c2ecf20Sopenharmony_ci res = 0; 11338c2ecf20Sopenharmony_ci for (idx = 0; idx < accum; idx++) { 11348c2ecf20Sopenharmony_ci if (res >= 0) { 11358c2ecf20Sopenharmony_ci res = sk_diag_fill(sk_arr[idx], skb, cb, r, 11368c2ecf20Sopenharmony_ci NLM_F_MULTI, net_admin); 11378c2ecf20Sopenharmony_ci if (res < 0) 11388c2ecf20Sopenharmony_ci num = num_arr[idx]; 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci sock_gen_put(sk_arr[idx]); 11418c2ecf20Sopenharmony_ci } 11428c2ecf20Sopenharmony_ci if (res < 0) 11438c2ecf20Sopenharmony_ci break; 11448c2ecf20Sopenharmony_ci cond_resched(); 11458c2ecf20Sopenharmony_ci if (accum == SKARR_SZ) { 11468c2ecf20Sopenharmony_ci s_num = num + 1; 11478c2ecf20Sopenharmony_ci goto next_chunk; 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci } 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_cidone: 11528c2ecf20Sopenharmony_ci cb->args[1] = i; 11538c2ecf20Sopenharmony_ci cb->args[2] = num; 11548c2ecf20Sopenharmony_ciout: 11558c2ecf20Sopenharmony_ci ; 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_diag_dump_icsk); 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_cistatic int __inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, 11608c2ecf20Sopenharmony_ci const struct inet_diag_req_v2 *r) 11618c2ecf20Sopenharmony_ci{ 11628c2ecf20Sopenharmony_ci struct inet_diag_dump_data *cb_data = cb->data; 11638c2ecf20Sopenharmony_ci const struct inet_diag_handler *handler; 11648c2ecf20Sopenharmony_ci u32 prev_min_dump_alloc; 11658c2ecf20Sopenharmony_ci int protocol, err = 0; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci protocol = inet_diag_get_protocol(r, cb_data); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ciagain: 11708c2ecf20Sopenharmony_ci prev_min_dump_alloc = cb->min_dump_alloc; 11718c2ecf20Sopenharmony_ci handler = inet_diag_lock_handler(protocol); 11728c2ecf20Sopenharmony_ci if (!IS_ERR(handler)) 11738c2ecf20Sopenharmony_ci handler->dump(skb, cb, r); 11748c2ecf20Sopenharmony_ci else 11758c2ecf20Sopenharmony_ci err = PTR_ERR(handler); 11768c2ecf20Sopenharmony_ci inet_diag_unlock_handler(handler); 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci /* The skb is not large enough to fit one sk info and 11798c2ecf20Sopenharmony_ci * inet_sk_diag_fill() has requested for a larger skb. 11808c2ecf20Sopenharmony_ci */ 11818c2ecf20Sopenharmony_ci if (!skb->len && cb->min_dump_alloc > prev_min_dump_alloc) { 11828c2ecf20Sopenharmony_ci err = pskb_expand_head(skb, 0, cb->min_dump_alloc, GFP_KERNEL); 11838c2ecf20Sopenharmony_ci if (!err) 11848c2ecf20Sopenharmony_ci goto again; 11858c2ecf20Sopenharmony_ci } 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci return err ? : skb->len; 11888c2ecf20Sopenharmony_ci} 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_cistatic int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) 11918c2ecf20Sopenharmony_ci{ 11928c2ecf20Sopenharmony_ci return __inet_diag_dump(skb, cb, nlmsg_data(cb->nlh)); 11938c2ecf20Sopenharmony_ci} 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_cistatic int __inet_diag_dump_start(struct netlink_callback *cb, int hdrlen) 11968c2ecf20Sopenharmony_ci{ 11978c2ecf20Sopenharmony_ci const struct nlmsghdr *nlh = cb->nlh; 11988c2ecf20Sopenharmony_ci struct inet_diag_dump_data *cb_data; 11998c2ecf20Sopenharmony_ci struct sk_buff *skb = cb->skb; 12008c2ecf20Sopenharmony_ci struct nlattr *nla; 12018c2ecf20Sopenharmony_ci int err; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci cb_data = kzalloc(sizeof(*cb_data), GFP_KERNEL); 12048c2ecf20Sopenharmony_ci if (!cb_data) 12058c2ecf20Sopenharmony_ci return -ENOMEM; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci err = inet_diag_parse_attrs(nlh, hdrlen, cb_data->req_nlas); 12088c2ecf20Sopenharmony_ci if (err) { 12098c2ecf20Sopenharmony_ci kfree(cb_data); 12108c2ecf20Sopenharmony_ci return err; 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci nla = cb_data->inet_diag_nla_bc; 12138c2ecf20Sopenharmony_ci if (nla) { 12148c2ecf20Sopenharmony_ci err = inet_diag_bc_audit(nla, skb); 12158c2ecf20Sopenharmony_ci if (err) { 12168c2ecf20Sopenharmony_ci kfree(cb_data); 12178c2ecf20Sopenharmony_ci return err; 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci } 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci nla = cb_data->inet_diag_nla_bpf_stgs; 12228c2ecf20Sopenharmony_ci if (nla) { 12238c2ecf20Sopenharmony_ci struct bpf_sk_storage_diag *bpf_stg_diag; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci bpf_stg_diag = bpf_sk_storage_diag_alloc(nla); 12268c2ecf20Sopenharmony_ci if (IS_ERR(bpf_stg_diag)) { 12278c2ecf20Sopenharmony_ci kfree(cb_data); 12288c2ecf20Sopenharmony_ci return PTR_ERR(bpf_stg_diag); 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci cb_data->bpf_stg_diag = bpf_stg_diag; 12318c2ecf20Sopenharmony_ci } 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci cb->data = cb_data; 12348c2ecf20Sopenharmony_ci return 0; 12358c2ecf20Sopenharmony_ci} 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_cistatic int inet_diag_dump_start(struct netlink_callback *cb) 12388c2ecf20Sopenharmony_ci{ 12398c2ecf20Sopenharmony_ci return __inet_diag_dump_start(cb, sizeof(struct inet_diag_req_v2)); 12408c2ecf20Sopenharmony_ci} 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_cistatic int inet_diag_dump_start_compat(struct netlink_callback *cb) 12438c2ecf20Sopenharmony_ci{ 12448c2ecf20Sopenharmony_ci return __inet_diag_dump_start(cb, sizeof(struct inet_diag_req)); 12458c2ecf20Sopenharmony_ci} 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_cistatic int inet_diag_dump_done(struct netlink_callback *cb) 12488c2ecf20Sopenharmony_ci{ 12498c2ecf20Sopenharmony_ci struct inet_diag_dump_data *cb_data = cb->data; 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci bpf_sk_storage_diag_free(cb_data->bpf_stg_diag); 12528c2ecf20Sopenharmony_ci kfree(cb->data); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci return 0; 12558c2ecf20Sopenharmony_ci} 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_cistatic int inet_diag_type2proto(int type) 12588c2ecf20Sopenharmony_ci{ 12598c2ecf20Sopenharmony_ci switch (type) { 12608c2ecf20Sopenharmony_ci case TCPDIAG_GETSOCK: 12618c2ecf20Sopenharmony_ci return IPPROTO_TCP; 12628c2ecf20Sopenharmony_ci case DCCPDIAG_GETSOCK: 12638c2ecf20Sopenharmony_ci return IPPROTO_DCCP; 12648c2ecf20Sopenharmony_ci default: 12658c2ecf20Sopenharmony_ci return 0; 12668c2ecf20Sopenharmony_ci } 12678c2ecf20Sopenharmony_ci} 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_cistatic int inet_diag_dump_compat(struct sk_buff *skb, 12708c2ecf20Sopenharmony_ci struct netlink_callback *cb) 12718c2ecf20Sopenharmony_ci{ 12728c2ecf20Sopenharmony_ci struct inet_diag_req *rc = nlmsg_data(cb->nlh); 12738c2ecf20Sopenharmony_ci struct inet_diag_req_v2 req; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci req.sdiag_family = AF_UNSPEC; /* compatibility */ 12768c2ecf20Sopenharmony_ci req.sdiag_protocol = inet_diag_type2proto(cb->nlh->nlmsg_type); 12778c2ecf20Sopenharmony_ci req.idiag_ext = rc->idiag_ext; 12788c2ecf20Sopenharmony_ci req.pad = 0; 12798c2ecf20Sopenharmony_ci req.idiag_states = rc->idiag_states; 12808c2ecf20Sopenharmony_ci req.id = rc->id; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci return __inet_diag_dump(skb, cb, &req); 12838c2ecf20Sopenharmony_ci} 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_cistatic int inet_diag_get_exact_compat(struct sk_buff *in_skb, 12868c2ecf20Sopenharmony_ci const struct nlmsghdr *nlh) 12878c2ecf20Sopenharmony_ci{ 12888c2ecf20Sopenharmony_ci struct inet_diag_req *rc = nlmsg_data(nlh); 12898c2ecf20Sopenharmony_ci struct inet_diag_req_v2 req; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci req.sdiag_family = rc->idiag_family; 12928c2ecf20Sopenharmony_ci req.sdiag_protocol = inet_diag_type2proto(nlh->nlmsg_type); 12938c2ecf20Sopenharmony_ci req.idiag_ext = rc->idiag_ext; 12948c2ecf20Sopenharmony_ci req.pad = 0; 12958c2ecf20Sopenharmony_ci req.idiag_states = rc->idiag_states; 12968c2ecf20Sopenharmony_ci req.id = rc->id; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci return inet_diag_cmd_exact(SOCK_DIAG_BY_FAMILY, in_skb, nlh, 12998c2ecf20Sopenharmony_ci sizeof(struct inet_diag_req), &req); 13008c2ecf20Sopenharmony_ci} 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_cistatic int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh) 13038c2ecf20Sopenharmony_ci{ 13048c2ecf20Sopenharmony_ci int hdrlen = sizeof(struct inet_diag_req); 13058c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX || 13088c2ecf20Sopenharmony_ci nlmsg_len(nlh) < hdrlen) 13098c2ecf20Sopenharmony_ci return -EINVAL; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci if (nlh->nlmsg_flags & NLM_F_DUMP) { 13128c2ecf20Sopenharmony_ci struct netlink_dump_control c = { 13138c2ecf20Sopenharmony_ci .start = inet_diag_dump_start_compat, 13148c2ecf20Sopenharmony_ci .done = inet_diag_dump_done, 13158c2ecf20Sopenharmony_ci .dump = inet_diag_dump_compat, 13168c2ecf20Sopenharmony_ci }; 13178c2ecf20Sopenharmony_ci return netlink_dump_start(net->diag_nlsk, skb, nlh, &c); 13188c2ecf20Sopenharmony_ci } 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci return inet_diag_get_exact_compat(skb, nlh); 13218c2ecf20Sopenharmony_ci} 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_cistatic int inet_diag_handler_cmd(struct sk_buff *skb, struct nlmsghdr *h) 13248c2ecf20Sopenharmony_ci{ 13258c2ecf20Sopenharmony_ci int hdrlen = sizeof(struct inet_diag_req_v2); 13268c2ecf20Sopenharmony_ci struct net *net = sock_net(skb->sk); 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci if (nlmsg_len(h) < hdrlen) 13298c2ecf20Sopenharmony_ci return -EINVAL; 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci if (h->nlmsg_type == SOCK_DIAG_BY_FAMILY && 13328c2ecf20Sopenharmony_ci h->nlmsg_flags & NLM_F_DUMP) { 13338c2ecf20Sopenharmony_ci struct netlink_dump_control c = { 13348c2ecf20Sopenharmony_ci .start = inet_diag_dump_start, 13358c2ecf20Sopenharmony_ci .done = inet_diag_dump_done, 13368c2ecf20Sopenharmony_ci .dump = inet_diag_dump, 13378c2ecf20Sopenharmony_ci }; 13388c2ecf20Sopenharmony_ci return netlink_dump_start(net->diag_nlsk, skb, h, &c); 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci return inet_diag_cmd_exact(h->nlmsg_type, skb, h, hdrlen, 13428c2ecf20Sopenharmony_ci nlmsg_data(h)); 13438c2ecf20Sopenharmony_ci} 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_cistatic 13468c2ecf20Sopenharmony_ciint inet_diag_handler_get_info(struct sk_buff *skb, struct sock *sk) 13478c2ecf20Sopenharmony_ci{ 13488c2ecf20Sopenharmony_ci const struct inet_diag_handler *handler; 13498c2ecf20Sopenharmony_ci struct nlmsghdr *nlh; 13508c2ecf20Sopenharmony_ci struct nlattr *attr; 13518c2ecf20Sopenharmony_ci struct inet_diag_msg *r; 13528c2ecf20Sopenharmony_ci void *info = NULL; 13538c2ecf20Sopenharmony_ci int err = 0; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci nlh = nlmsg_put(skb, 0, 0, SOCK_DIAG_BY_FAMILY, sizeof(*r), 0); 13568c2ecf20Sopenharmony_ci if (!nlh) 13578c2ecf20Sopenharmony_ci return -ENOMEM; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci r = nlmsg_data(nlh); 13608c2ecf20Sopenharmony_ci memset(r, 0, sizeof(*r)); 13618c2ecf20Sopenharmony_ci inet_diag_msg_common_fill(r, sk); 13628c2ecf20Sopenharmony_ci if (sk->sk_type == SOCK_DGRAM || sk->sk_type == SOCK_STREAM) 13638c2ecf20Sopenharmony_ci r->id.idiag_sport = inet_sk(sk)->inet_sport; 13648c2ecf20Sopenharmony_ci r->idiag_state = sk->sk_state; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci if ((err = nla_put_u8(skb, INET_DIAG_PROTOCOL, sk->sk_protocol))) { 13678c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 13688c2ecf20Sopenharmony_ci return err; 13698c2ecf20Sopenharmony_ci } 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci handler = inet_diag_lock_handler(sk->sk_protocol); 13728c2ecf20Sopenharmony_ci if (IS_ERR(handler)) { 13738c2ecf20Sopenharmony_ci inet_diag_unlock_handler(handler); 13748c2ecf20Sopenharmony_ci nlmsg_cancel(skb, nlh); 13758c2ecf20Sopenharmony_ci return PTR_ERR(handler); 13768c2ecf20Sopenharmony_ci } 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci attr = handler->idiag_info_size 13798c2ecf20Sopenharmony_ci ? nla_reserve_64bit(skb, INET_DIAG_INFO, 13808c2ecf20Sopenharmony_ci handler->idiag_info_size, 13818c2ecf20Sopenharmony_ci INET_DIAG_PAD) 13828c2ecf20Sopenharmony_ci : NULL; 13838c2ecf20Sopenharmony_ci if (attr) 13848c2ecf20Sopenharmony_ci info = nla_data(attr); 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci handler->idiag_get_info(sk, r, info); 13878c2ecf20Sopenharmony_ci inet_diag_unlock_handler(handler); 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci nlmsg_end(skb, nlh); 13908c2ecf20Sopenharmony_ci return 0; 13918c2ecf20Sopenharmony_ci} 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_cistatic const struct sock_diag_handler inet_diag_handler = { 13948c2ecf20Sopenharmony_ci .family = AF_INET, 13958c2ecf20Sopenharmony_ci .dump = inet_diag_handler_cmd, 13968c2ecf20Sopenharmony_ci .get_info = inet_diag_handler_get_info, 13978c2ecf20Sopenharmony_ci .destroy = inet_diag_handler_cmd, 13988c2ecf20Sopenharmony_ci}; 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_cistatic const struct sock_diag_handler inet6_diag_handler = { 14018c2ecf20Sopenharmony_ci .family = AF_INET6, 14028c2ecf20Sopenharmony_ci .dump = inet_diag_handler_cmd, 14038c2ecf20Sopenharmony_ci .get_info = inet_diag_handler_get_info, 14048c2ecf20Sopenharmony_ci .destroy = inet_diag_handler_cmd, 14058c2ecf20Sopenharmony_ci}; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ciint inet_diag_register(const struct inet_diag_handler *h) 14088c2ecf20Sopenharmony_ci{ 14098c2ecf20Sopenharmony_ci const __u16 type = h->idiag_type; 14108c2ecf20Sopenharmony_ci int err = -EINVAL; 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci if (type >= IPPROTO_MAX) 14138c2ecf20Sopenharmony_ci goto out; 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci mutex_lock(&inet_diag_table_mutex); 14168c2ecf20Sopenharmony_ci err = -EEXIST; 14178c2ecf20Sopenharmony_ci if (!inet_diag_table[type]) { 14188c2ecf20Sopenharmony_ci inet_diag_table[type] = h; 14198c2ecf20Sopenharmony_ci err = 0; 14208c2ecf20Sopenharmony_ci } 14218c2ecf20Sopenharmony_ci mutex_unlock(&inet_diag_table_mutex); 14228c2ecf20Sopenharmony_ciout: 14238c2ecf20Sopenharmony_ci return err; 14248c2ecf20Sopenharmony_ci} 14258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_diag_register); 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_civoid inet_diag_unregister(const struct inet_diag_handler *h) 14288c2ecf20Sopenharmony_ci{ 14298c2ecf20Sopenharmony_ci const __u16 type = h->idiag_type; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci if (type >= IPPROTO_MAX) 14328c2ecf20Sopenharmony_ci return; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci mutex_lock(&inet_diag_table_mutex); 14358c2ecf20Sopenharmony_ci inet_diag_table[type] = NULL; 14368c2ecf20Sopenharmony_ci mutex_unlock(&inet_diag_table_mutex); 14378c2ecf20Sopenharmony_ci} 14388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_diag_unregister); 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_cistatic int __init inet_diag_init(void) 14418c2ecf20Sopenharmony_ci{ 14428c2ecf20Sopenharmony_ci const int inet_diag_table_size = (IPPROTO_MAX * 14438c2ecf20Sopenharmony_ci sizeof(struct inet_diag_handler *)); 14448c2ecf20Sopenharmony_ci int err = -ENOMEM; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci inet_diag_table = kzalloc(inet_diag_table_size, GFP_KERNEL); 14478c2ecf20Sopenharmony_ci if (!inet_diag_table) 14488c2ecf20Sopenharmony_ci goto out; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci err = sock_diag_register(&inet_diag_handler); 14518c2ecf20Sopenharmony_ci if (err) 14528c2ecf20Sopenharmony_ci goto out_free_nl; 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci err = sock_diag_register(&inet6_diag_handler); 14558c2ecf20Sopenharmony_ci if (err) 14568c2ecf20Sopenharmony_ci goto out_free_inet; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci sock_diag_register_inet_compat(inet_diag_rcv_msg_compat); 14598c2ecf20Sopenharmony_ciout: 14608c2ecf20Sopenharmony_ci return err; 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ciout_free_inet: 14638c2ecf20Sopenharmony_ci sock_diag_unregister(&inet_diag_handler); 14648c2ecf20Sopenharmony_ciout_free_nl: 14658c2ecf20Sopenharmony_ci kfree(inet_diag_table); 14668c2ecf20Sopenharmony_ci goto out; 14678c2ecf20Sopenharmony_ci} 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_cistatic void __exit inet_diag_exit(void) 14708c2ecf20Sopenharmony_ci{ 14718c2ecf20Sopenharmony_ci sock_diag_unregister(&inet6_diag_handler); 14728c2ecf20Sopenharmony_ci sock_diag_unregister(&inet_diag_handler); 14738c2ecf20Sopenharmony_ci sock_diag_unregister_inet_compat(inet_diag_rcv_msg_compat); 14748c2ecf20Sopenharmony_ci kfree(inet_diag_table); 14758c2ecf20Sopenharmony_ci} 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_cimodule_init(inet_diag_init); 14788c2ecf20Sopenharmony_cimodule_exit(inet_diag_exit); 14798c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 14808c2ecf20Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2 /* AF_INET */); 14818c2ecf20Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 10 /* AF_INET6 */); 1482