162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * inet_diag.c Module for monitoring INET transport protocols sockets. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/types.h> 1162306a36Sopenharmony_ci#include <linux/fcntl.h> 1262306a36Sopenharmony_ci#include <linux/random.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/cache.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/time.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <net/icmp.h> 1962306a36Sopenharmony_ci#include <net/tcp.h> 2062306a36Sopenharmony_ci#include <net/ipv6.h> 2162306a36Sopenharmony_ci#include <net/inet_common.h> 2262306a36Sopenharmony_ci#include <net/inet_connection_sock.h> 2362306a36Sopenharmony_ci#include <net/inet_hashtables.h> 2462306a36Sopenharmony_ci#include <net/inet_timewait_sock.h> 2562306a36Sopenharmony_ci#include <net/inet6_hashtables.h> 2662306a36Sopenharmony_ci#include <net/bpf_sk_storage.h> 2762306a36Sopenharmony_ci#include <net/netlink.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/inet.h> 3062306a36Sopenharmony_ci#include <linux/stddef.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include <linux/inet_diag.h> 3362306a36Sopenharmony_ci#include <linux/sock_diag.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic const struct inet_diag_handler **inet_diag_table; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct inet_diag_entry { 3862306a36Sopenharmony_ci const __be32 *saddr; 3962306a36Sopenharmony_ci const __be32 *daddr; 4062306a36Sopenharmony_ci u16 sport; 4162306a36Sopenharmony_ci u16 dport; 4262306a36Sopenharmony_ci u16 family; 4362306a36Sopenharmony_ci u16 userlocks; 4462306a36Sopenharmony_ci u32 ifindex; 4562306a36Sopenharmony_ci u32 mark; 4662306a36Sopenharmony_ci#ifdef CONFIG_SOCK_CGROUP_DATA 4762306a36Sopenharmony_ci u64 cgroup_id; 4862306a36Sopenharmony_ci#endif 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic DEFINE_MUTEX(inet_diag_table_mutex); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic const struct inet_diag_handler *inet_diag_lock_handler(int proto) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci if (proto < 0 || proto >= IPPROTO_MAX) { 5662306a36Sopenharmony_ci mutex_lock(&inet_diag_table_mutex); 5762306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci if (!READ_ONCE(inet_diag_table[proto])) 6162306a36Sopenharmony_ci sock_load_diag_module(AF_INET, proto); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci mutex_lock(&inet_diag_table_mutex); 6462306a36Sopenharmony_ci if (!inet_diag_table[proto]) 6562306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return inet_diag_table[proto]; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic void inet_diag_unlock_handler(const struct inet_diag_handler *handler) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci mutex_unlock(&inet_diag_table_mutex); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_civoid inet_diag_msg_common_fill(struct inet_diag_msg *r, struct sock *sk) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci r->idiag_family = sk->sk_family; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci r->id.idiag_sport = htons(sk->sk_num); 8062306a36Sopenharmony_ci r->id.idiag_dport = sk->sk_dport; 8162306a36Sopenharmony_ci r->id.idiag_if = sk->sk_bound_dev_if; 8262306a36Sopenharmony_ci sock_diag_save_cookie(sk, r->id.idiag_cookie); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 8562306a36Sopenharmony_ci if (sk->sk_family == AF_INET6) { 8662306a36Sopenharmony_ci *(struct in6_addr *)r->id.idiag_src = sk->sk_v6_rcv_saddr; 8762306a36Sopenharmony_ci *(struct in6_addr *)r->id.idiag_dst = sk->sk_v6_daddr; 8862306a36Sopenharmony_ci } else 8962306a36Sopenharmony_ci#endif 9062306a36Sopenharmony_ci { 9162306a36Sopenharmony_ci memset(&r->id.idiag_src, 0, sizeof(r->id.idiag_src)); 9262306a36Sopenharmony_ci memset(&r->id.idiag_dst, 0, sizeof(r->id.idiag_dst)); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci r->id.idiag_src[0] = sk->sk_rcv_saddr; 9562306a36Sopenharmony_ci r->id.idiag_dst[0] = sk->sk_daddr; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_diag_msg_common_fill); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic size_t inet_sk_attr_size(struct sock *sk, 10162306a36Sopenharmony_ci const struct inet_diag_req_v2 *req, 10262306a36Sopenharmony_ci bool net_admin) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci const struct inet_diag_handler *handler; 10562306a36Sopenharmony_ci size_t aux = 0; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci handler = inet_diag_table[req->sdiag_protocol]; 10862306a36Sopenharmony_ci if (handler && handler->idiag_get_aux_size) 10962306a36Sopenharmony_ci aux = handler->idiag_get_aux_size(sk, net_admin); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci return nla_total_size(sizeof(struct tcp_info)) 11262306a36Sopenharmony_ci + nla_total_size(sizeof(struct inet_diag_msg)) 11362306a36Sopenharmony_ci + inet_diag_msg_attrs_size() 11462306a36Sopenharmony_ci + nla_total_size(sizeof(struct inet_diag_meminfo)) 11562306a36Sopenharmony_ci + nla_total_size(SK_MEMINFO_VARS * sizeof(u32)) 11662306a36Sopenharmony_ci + nla_total_size(TCP_CA_NAME_MAX) 11762306a36Sopenharmony_ci + nla_total_size(sizeof(struct tcpvegas_info)) 11862306a36Sopenharmony_ci + aux 11962306a36Sopenharmony_ci + 64; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ciint inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb, 12362306a36Sopenharmony_ci struct inet_diag_msg *r, int ext, 12462306a36Sopenharmony_ci struct user_namespace *user_ns, 12562306a36Sopenharmony_ci bool net_admin) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci const struct inet_sock *inet = inet_sk(sk); 12862306a36Sopenharmony_ci struct inet_diag_sockopt inet_sockopt; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (nla_put_u8(skb, INET_DIAG_SHUTDOWN, sk->sk_shutdown)) 13162306a36Sopenharmony_ci goto errout; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* IPv6 dual-stack sockets use inet->tos for IPv4 connections, 13462306a36Sopenharmony_ci * hence this needs to be included regardless of socket family. 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci if (ext & (1 << (INET_DIAG_TOS - 1))) 13762306a36Sopenharmony_ci if (nla_put_u8(skb, INET_DIAG_TOS, inet->tos) < 0) 13862306a36Sopenharmony_ci goto errout; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 14162306a36Sopenharmony_ci if (r->idiag_family == AF_INET6) { 14262306a36Sopenharmony_ci if (ext & (1 << (INET_DIAG_TCLASS - 1))) 14362306a36Sopenharmony_ci if (nla_put_u8(skb, INET_DIAG_TCLASS, 14462306a36Sopenharmony_ci inet6_sk(sk)->tclass) < 0) 14562306a36Sopenharmony_ci goto errout; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) && 14862306a36Sopenharmony_ci nla_put_u8(skb, INET_DIAG_SKV6ONLY, ipv6_only_sock(sk))) 14962306a36Sopenharmony_ci goto errout; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci#endif 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (net_admin && nla_put_u32(skb, INET_DIAG_MARK, READ_ONCE(sk->sk_mark))) 15462306a36Sopenharmony_ci goto errout; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (ext & (1 << (INET_DIAG_CLASS_ID - 1)) || 15762306a36Sopenharmony_ci ext & (1 << (INET_DIAG_TCLASS - 1))) { 15862306a36Sopenharmony_ci u32 classid = 0; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci#ifdef CONFIG_SOCK_CGROUP_DATA 16162306a36Sopenharmony_ci classid = sock_cgroup_classid(&sk->sk_cgrp_data); 16262306a36Sopenharmony_ci#endif 16362306a36Sopenharmony_ci /* Fallback to socket priority if class id isn't set. 16462306a36Sopenharmony_ci * Classful qdiscs use it as direct reference to class. 16562306a36Sopenharmony_ci * For cgroup2 classid is always zero. 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci if (!classid) 16862306a36Sopenharmony_ci classid = sk->sk_priority; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (nla_put_u32(skb, INET_DIAG_CLASS_ID, classid)) 17162306a36Sopenharmony_ci goto errout; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci#ifdef CONFIG_SOCK_CGROUP_DATA 17562306a36Sopenharmony_ci if (nla_put_u64_64bit(skb, INET_DIAG_CGROUP_ID, 17662306a36Sopenharmony_ci cgroup_id(sock_cgroup_ptr(&sk->sk_cgrp_data)), 17762306a36Sopenharmony_ci INET_DIAG_PAD)) 17862306a36Sopenharmony_ci goto errout; 17962306a36Sopenharmony_ci#endif 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci r->idiag_uid = from_kuid_munged(user_ns, sock_i_uid(sk)); 18262306a36Sopenharmony_ci r->idiag_inode = sock_i_ino(sk); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci memset(&inet_sockopt, 0, sizeof(inet_sockopt)); 18562306a36Sopenharmony_ci inet_sockopt.recverr = inet_test_bit(RECVERR, sk); 18662306a36Sopenharmony_ci inet_sockopt.is_icsk = inet_test_bit(IS_ICSK, sk); 18762306a36Sopenharmony_ci inet_sockopt.freebind = inet_test_bit(FREEBIND, sk); 18862306a36Sopenharmony_ci inet_sockopt.hdrincl = inet_test_bit(HDRINCL, sk); 18962306a36Sopenharmony_ci inet_sockopt.mc_loop = inet_test_bit(MC_LOOP, sk); 19062306a36Sopenharmony_ci inet_sockopt.transparent = inet_test_bit(TRANSPARENT, sk); 19162306a36Sopenharmony_ci inet_sockopt.mc_all = inet_test_bit(MC_ALL, sk); 19262306a36Sopenharmony_ci inet_sockopt.nodefrag = inet_test_bit(NODEFRAG, sk); 19362306a36Sopenharmony_ci inet_sockopt.bind_address_no_port = inet_test_bit(BIND_ADDRESS_NO_PORT, sk); 19462306a36Sopenharmony_ci inet_sockopt.recverr_rfc4884 = inet_test_bit(RECVERR_RFC4884, sk); 19562306a36Sopenharmony_ci inet_sockopt.defer_connect = inet_test_bit(DEFER_CONNECT, sk); 19662306a36Sopenharmony_ci if (nla_put(skb, INET_DIAG_SOCKOPT, sizeof(inet_sockopt), 19762306a36Sopenharmony_ci &inet_sockopt)) 19862306a36Sopenharmony_ci goto errout; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return 0; 20162306a36Sopenharmony_cierrout: 20262306a36Sopenharmony_ci return 1; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_diag_msg_attrs_fill); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int inet_diag_parse_attrs(const struct nlmsghdr *nlh, int hdrlen, 20762306a36Sopenharmony_ci struct nlattr **req_nlas) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct nlattr *nla; 21062306a36Sopenharmony_ci int remaining; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci nlmsg_for_each_attr(nla, nlh, hdrlen, remaining) { 21362306a36Sopenharmony_ci int type = nla_type(nla); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (type == INET_DIAG_REQ_PROTOCOL && nla_len(nla) != sizeof(u32)) 21662306a36Sopenharmony_ci return -EINVAL; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (type < __INET_DIAG_REQ_MAX) 21962306a36Sopenharmony_ci req_nlas[type] = nla; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci return 0; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int inet_diag_get_protocol(const struct inet_diag_req_v2 *req, 22562306a36Sopenharmony_ci const struct inet_diag_dump_data *data) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci if (data->req_nlas[INET_DIAG_REQ_PROTOCOL]) 22862306a36Sopenharmony_ci return nla_get_u32(data->req_nlas[INET_DIAG_REQ_PROTOCOL]); 22962306a36Sopenharmony_ci return req->sdiag_protocol; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci#define MAX_DUMP_ALLOC_SIZE (KMALLOC_MAX_SIZE - SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ciint inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, 23562306a36Sopenharmony_ci struct sk_buff *skb, struct netlink_callback *cb, 23662306a36Sopenharmony_ci const struct inet_diag_req_v2 *req, 23762306a36Sopenharmony_ci u16 nlmsg_flags, bool net_admin) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci const struct tcp_congestion_ops *ca_ops; 24062306a36Sopenharmony_ci const struct inet_diag_handler *handler; 24162306a36Sopenharmony_ci struct inet_diag_dump_data *cb_data; 24262306a36Sopenharmony_ci int ext = req->idiag_ext; 24362306a36Sopenharmony_ci struct inet_diag_msg *r; 24462306a36Sopenharmony_ci struct nlmsghdr *nlh; 24562306a36Sopenharmony_ci struct nlattr *attr; 24662306a36Sopenharmony_ci void *info = NULL; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci cb_data = cb->data; 24962306a36Sopenharmony_ci handler = inet_diag_table[inet_diag_get_protocol(req, cb_data)]; 25062306a36Sopenharmony_ci BUG_ON(!handler); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 25362306a36Sopenharmony_ci cb->nlh->nlmsg_type, sizeof(*r), nlmsg_flags); 25462306a36Sopenharmony_ci if (!nlh) 25562306a36Sopenharmony_ci return -EMSGSIZE; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci r = nlmsg_data(nlh); 25862306a36Sopenharmony_ci BUG_ON(!sk_fullsock(sk)); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci inet_diag_msg_common_fill(r, sk); 26162306a36Sopenharmony_ci r->idiag_state = sk->sk_state; 26262306a36Sopenharmony_ci r->idiag_timer = 0; 26362306a36Sopenharmony_ci r->idiag_retrans = 0; 26462306a36Sopenharmony_ci r->idiag_expires = 0; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (inet_diag_msg_attrs_fill(sk, skb, r, ext, 26762306a36Sopenharmony_ci sk_user_ns(NETLINK_CB(cb->skb).sk), 26862306a36Sopenharmony_ci net_admin)) 26962306a36Sopenharmony_ci goto errout; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (ext & (1 << (INET_DIAG_MEMINFO - 1))) { 27262306a36Sopenharmony_ci struct inet_diag_meminfo minfo = { 27362306a36Sopenharmony_ci .idiag_rmem = sk_rmem_alloc_get(sk), 27462306a36Sopenharmony_ci .idiag_wmem = READ_ONCE(sk->sk_wmem_queued), 27562306a36Sopenharmony_ci .idiag_fmem = sk_forward_alloc_get(sk), 27662306a36Sopenharmony_ci .idiag_tmem = sk_wmem_alloc_get(sk), 27762306a36Sopenharmony_ci }; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (nla_put(skb, INET_DIAG_MEMINFO, sizeof(minfo), &minfo) < 0) 28062306a36Sopenharmony_ci goto errout; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (ext & (1 << (INET_DIAG_SKMEMINFO - 1))) 28462306a36Sopenharmony_ci if (sock_diag_put_meminfo(sk, skb, INET_DIAG_SKMEMINFO)) 28562306a36Sopenharmony_ci goto errout; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* 28862306a36Sopenharmony_ci * RAW sockets might have user-defined protocols assigned, 28962306a36Sopenharmony_ci * so report the one supplied on socket creation. 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_ci if (sk->sk_type == SOCK_RAW) { 29262306a36Sopenharmony_ci if (nla_put_u8(skb, INET_DIAG_PROTOCOL, sk->sk_protocol)) 29362306a36Sopenharmony_ci goto errout; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (!icsk) { 29762306a36Sopenharmony_ci handler->idiag_get_info(sk, r, NULL); 29862306a36Sopenharmony_ci goto out; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (icsk->icsk_pending == ICSK_TIME_RETRANS || 30262306a36Sopenharmony_ci icsk->icsk_pending == ICSK_TIME_REO_TIMEOUT || 30362306a36Sopenharmony_ci icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) { 30462306a36Sopenharmony_ci r->idiag_timer = 1; 30562306a36Sopenharmony_ci r->idiag_retrans = icsk->icsk_retransmits; 30662306a36Sopenharmony_ci r->idiag_expires = 30762306a36Sopenharmony_ci jiffies_delta_to_msecs(icsk->icsk_timeout - jiffies); 30862306a36Sopenharmony_ci } else if (icsk->icsk_pending == ICSK_TIME_PROBE0) { 30962306a36Sopenharmony_ci r->idiag_timer = 4; 31062306a36Sopenharmony_ci r->idiag_retrans = icsk->icsk_probes_out; 31162306a36Sopenharmony_ci r->idiag_expires = 31262306a36Sopenharmony_ci jiffies_delta_to_msecs(icsk->icsk_timeout - jiffies); 31362306a36Sopenharmony_ci } else if (timer_pending(&sk->sk_timer)) { 31462306a36Sopenharmony_ci r->idiag_timer = 2; 31562306a36Sopenharmony_ci r->idiag_retrans = icsk->icsk_probes_out; 31662306a36Sopenharmony_ci r->idiag_expires = 31762306a36Sopenharmony_ci jiffies_delta_to_msecs(sk->sk_timer.expires - jiffies); 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if ((ext & (1 << (INET_DIAG_INFO - 1))) && handler->idiag_info_size) { 32162306a36Sopenharmony_ci attr = nla_reserve_64bit(skb, INET_DIAG_INFO, 32262306a36Sopenharmony_ci handler->idiag_info_size, 32362306a36Sopenharmony_ci INET_DIAG_PAD); 32462306a36Sopenharmony_ci if (!attr) 32562306a36Sopenharmony_ci goto errout; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci info = nla_data(attr); 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (ext & (1 << (INET_DIAG_CONG - 1))) { 33162306a36Sopenharmony_ci int err = 0; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci rcu_read_lock(); 33462306a36Sopenharmony_ci ca_ops = READ_ONCE(icsk->icsk_ca_ops); 33562306a36Sopenharmony_ci if (ca_ops) 33662306a36Sopenharmony_ci err = nla_put_string(skb, INET_DIAG_CONG, ca_ops->name); 33762306a36Sopenharmony_ci rcu_read_unlock(); 33862306a36Sopenharmony_ci if (err < 0) 33962306a36Sopenharmony_ci goto errout; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci handler->idiag_get_info(sk, r, info); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (ext & (1 << (INET_DIAG_INFO - 1)) && handler->idiag_get_aux) 34562306a36Sopenharmony_ci if (handler->idiag_get_aux(sk, net_admin, skb) < 0) 34662306a36Sopenharmony_ci goto errout; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (sk->sk_state < TCP_TIME_WAIT) { 34962306a36Sopenharmony_ci union tcp_cc_info info; 35062306a36Sopenharmony_ci size_t sz = 0; 35162306a36Sopenharmony_ci int attr; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci rcu_read_lock(); 35462306a36Sopenharmony_ci ca_ops = READ_ONCE(icsk->icsk_ca_ops); 35562306a36Sopenharmony_ci if (ca_ops && ca_ops->get_info) 35662306a36Sopenharmony_ci sz = ca_ops->get_info(sk, ext, &attr, &info); 35762306a36Sopenharmony_ci rcu_read_unlock(); 35862306a36Sopenharmony_ci if (sz && nla_put(skb, attr, sz, &info) < 0) 35962306a36Sopenharmony_ci goto errout; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* Keep it at the end for potential retry with a larger skb, 36362306a36Sopenharmony_ci * or else do best-effort fitting, which is only done for the 36462306a36Sopenharmony_ci * first_nlmsg. 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_ci if (cb_data->bpf_stg_diag) { 36762306a36Sopenharmony_ci bool first_nlmsg = ((unsigned char *)nlh == skb->data); 36862306a36Sopenharmony_ci unsigned int prev_min_dump_alloc; 36962306a36Sopenharmony_ci unsigned int total_nla_size = 0; 37062306a36Sopenharmony_ci unsigned int msg_len; 37162306a36Sopenharmony_ci int err; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci msg_len = skb_tail_pointer(skb) - (unsigned char *)nlh; 37462306a36Sopenharmony_ci err = bpf_sk_storage_diag_put(cb_data->bpf_stg_diag, sk, skb, 37562306a36Sopenharmony_ci INET_DIAG_SK_BPF_STORAGES, 37662306a36Sopenharmony_ci &total_nla_size); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (!err) 37962306a36Sopenharmony_ci goto out; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci total_nla_size += msg_len; 38262306a36Sopenharmony_ci prev_min_dump_alloc = cb->min_dump_alloc; 38362306a36Sopenharmony_ci if (total_nla_size > prev_min_dump_alloc) 38462306a36Sopenharmony_ci cb->min_dump_alloc = min_t(u32, total_nla_size, 38562306a36Sopenharmony_ci MAX_DUMP_ALLOC_SIZE); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (!first_nlmsg) 38862306a36Sopenharmony_ci goto errout; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (cb->min_dump_alloc > prev_min_dump_alloc) 39162306a36Sopenharmony_ci /* Retry with pskb_expand_head() with 39262306a36Sopenharmony_ci * __GFP_DIRECT_RECLAIM 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_ci goto errout; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci WARN_ON_ONCE(total_nla_size <= prev_min_dump_alloc); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* Send what we have for this sk 39962306a36Sopenharmony_ci * and move on to the next sk in the following 40062306a36Sopenharmony_ci * dump() 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ciout: 40562306a36Sopenharmony_ci nlmsg_end(skb, nlh); 40662306a36Sopenharmony_ci return 0; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cierrout: 40962306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 41062306a36Sopenharmony_ci return -EMSGSIZE; 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_sk_diag_fill); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic int inet_twsk_diag_fill(struct sock *sk, 41562306a36Sopenharmony_ci struct sk_buff *skb, 41662306a36Sopenharmony_ci struct netlink_callback *cb, 41762306a36Sopenharmony_ci u16 nlmsg_flags, bool net_admin) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct inet_timewait_sock *tw = inet_twsk(sk); 42062306a36Sopenharmony_ci struct inet_diag_msg *r; 42162306a36Sopenharmony_ci struct nlmsghdr *nlh; 42262306a36Sopenharmony_ci long tmo; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, 42562306a36Sopenharmony_ci cb->nlh->nlmsg_seq, cb->nlh->nlmsg_type, 42662306a36Sopenharmony_ci sizeof(*r), nlmsg_flags); 42762306a36Sopenharmony_ci if (!nlh) 42862306a36Sopenharmony_ci return -EMSGSIZE; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci r = nlmsg_data(nlh); 43162306a36Sopenharmony_ci BUG_ON(tw->tw_state != TCP_TIME_WAIT); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci inet_diag_msg_common_fill(r, sk); 43462306a36Sopenharmony_ci r->idiag_retrans = 0; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci r->idiag_state = tw->tw_substate; 43762306a36Sopenharmony_ci r->idiag_timer = 3; 43862306a36Sopenharmony_ci tmo = tw->tw_timer.expires - jiffies; 43962306a36Sopenharmony_ci r->idiag_expires = jiffies_delta_to_msecs(tmo); 44062306a36Sopenharmony_ci r->idiag_rqueue = 0; 44162306a36Sopenharmony_ci r->idiag_wqueue = 0; 44262306a36Sopenharmony_ci r->idiag_uid = 0; 44362306a36Sopenharmony_ci r->idiag_inode = 0; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (net_admin && nla_put_u32(skb, INET_DIAG_MARK, 44662306a36Sopenharmony_ci tw->tw_mark)) { 44762306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 44862306a36Sopenharmony_ci return -EMSGSIZE; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci nlmsg_end(skb, nlh); 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic int inet_req_diag_fill(struct sock *sk, struct sk_buff *skb, 45662306a36Sopenharmony_ci struct netlink_callback *cb, 45762306a36Sopenharmony_ci u16 nlmsg_flags, bool net_admin) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci struct request_sock *reqsk = inet_reqsk(sk); 46062306a36Sopenharmony_ci struct inet_diag_msg *r; 46162306a36Sopenharmony_ci struct nlmsghdr *nlh; 46262306a36Sopenharmony_ci long tmo; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 46562306a36Sopenharmony_ci cb->nlh->nlmsg_type, sizeof(*r), nlmsg_flags); 46662306a36Sopenharmony_ci if (!nlh) 46762306a36Sopenharmony_ci return -EMSGSIZE; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci r = nlmsg_data(nlh); 47062306a36Sopenharmony_ci inet_diag_msg_common_fill(r, sk); 47162306a36Sopenharmony_ci r->idiag_state = TCP_SYN_RECV; 47262306a36Sopenharmony_ci r->idiag_timer = 1; 47362306a36Sopenharmony_ci r->idiag_retrans = reqsk->num_retrans; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(struct inet_request_sock, ir_cookie) != 47662306a36Sopenharmony_ci offsetof(struct sock, sk_cookie)); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci tmo = inet_reqsk(sk)->rsk_timer.expires - jiffies; 47962306a36Sopenharmony_ci r->idiag_expires = jiffies_delta_to_msecs(tmo); 48062306a36Sopenharmony_ci r->idiag_rqueue = 0; 48162306a36Sopenharmony_ci r->idiag_wqueue = 0; 48262306a36Sopenharmony_ci r->idiag_uid = 0; 48362306a36Sopenharmony_ci r->idiag_inode = 0; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci if (net_admin && nla_put_u32(skb, INET_DIAG_MARK, 48662306a36Sopenharmony_ci inet_rsk(reqsk)->ir_mark)) { 48762306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 48862306a36Sopenharmony_ci return -EMSGSIZE; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci nlmsg_end(skb, nlh); 49262306a36Sopenharmony_ci return 0; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic int sk_diag_fill(struct sock *sk, struct sk_buff *skb, 49662306a36Sopenharmony_ci struct netlink_callback *cb, 49762306a36Sopenharmony_ci const struct inet_diag_req_v2 *r, 49862306a36Sopenharmony_ci u16 nlmsg_flags, bool net_admin) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci if (sk->sk_state == TCP_TIME_WAIT) 50162306a36Sopenharmony_ci return inet_twsk_diag_fill(sk, skb, cb, nlmsg_flags, net_admin); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (sk->sk_state == TCP_NEW_SYN_RECV) 50462306a36Sopenharmony_ci return inet_req_diag_fill(sk, skb, cb, nlmsg_flags, net_admin); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci return inet_sk_diag_fill(sk, inet_csk(sk), skb, cb, r, nlmsg_flags, 50762306a36Sopenharmony_ci net_admin); 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistruct sock *inet_diag_find_one_icsk(struct net *net, 51162306a36Sopenharmony_ci struct inet_hashinfo *hashinfo, 51262306a36Sopenharmony_ci const struct inet_diag_req_v2 *req) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci struct sock *sk; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci rcu_read_lock(); 51762306a36Sopenharmony_ci if (req->sdiag_family == AF_INET) 51862306a36Sopenharmony_ci sk = inet_lookup(net, hashinfo, NULL, 0, req->id.idiag_dst[0], 51962306a36Sopenharmony_ci req->id.idiag_dport, req->id.idiag_src[0], 52062306a36Sopenharmony_ci req->id.idiag_sport, req->id.idiag_if); 52162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 52262306a36Sopenharmony_ci else if (req->sdiag_family == AF_INET6) { 52362306a36Sopenharmony_ci if (ipv6_addr_v4mapped((struct in6_addr *)req->id.idiag_dst) && 52462306a36Sopenharmony_ci ipv6_addr_v4mapped((struct in6_addr *)req->id.idiag_src)) 52562306a36Sopenharmony_ci sk = inet_lookup(net, hashinfo, NULL, 0, req->id.idiag_dst[3], 52662306a36Sopenharmony_ci req->id.idiag_dport, req->id.idiag_src[3], 52762306a36Sopenharmony_ci req->id.idiag_sport, req->id.idiag_if); 52862306a36Sopenharmony_ci else 52962306a36Sopenharmony_ci sk = inet6_lookup(net, hashinfo, NULL, 0, 53062306a36Sopenharmony_ci (struct in6_addr *)req->id.idiag_dst, 53162306a36Sopenharmony_ci req->id.idiag_dport, 53262306a36Sopenharmony_ci (struct in6_addr *)req->id.idiag_src, 53362306a36Sopenharmony_ci req->id.idiag_sport, 53462306a36Sopenharmony_ci req->id.idiag_if); 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci#endif 53762306a36Sopenharmony_ci else { 53862306a36Sopenharmony_ci rcu_read_unlock(); 53962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci rcu_read_unlock(); 54262306a36Sopenharmony_ci if (!sk) 54362306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (sock_diag_check_cookie(sk, req->id.idiag_cookie)) { 54662306a36Sopenharmony_ci sock_gen_put(sk); 54762306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci return sk; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_diag_find_one_icsk); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ciint inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, 55562306a36Sopenharmony_ci struct netlink_callback *cb, 55662306a36Sopenharmony_ci const struct inet_diag_req_v2 *req) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci struct sk_buff *in_skb = cb->skb; 55962306a36Sopenharmony_ci bool net_admin = netlink_net_capable(in_skb, CAP_NET_ADMIN); 56062306a36Sopenharmony_ci struct net *net = sock_net(in_skb->sk); 56162306a36Sopenharmony_ci struct sk_buff *rep; 56262306a36Sopenharmony_ci struct sock *sk; 56362306a36Sopenharmony_ci int err; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci sk = inet_diag_find_one_icsk(net, hashinfo, req); 56662306a36Sopenharmony_ci if (IS_ERR(sk)) 56762306a36Sopenharmony_ci return PTR_ERR(sk); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci rep = nlmsg_new(inet_sk_attr_size(sk, req, net_admin), GFP_KERNEL); 57062306a36Sopenharmony_ci if (!rep) { 57162306a36Sopenharmony_ci err = -ENOMEM; 57262306a36Sopenharmony_ci goto out; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci err = sk_diag_fill(sk, rep, cb, req, 0, net_admin); 57662306a36Sopenharmony_ci if (err < 0) { 57762306a36Sopenharmony_ci WARN_ON(err == -EMSGSIZE); 57862306a36Sopenharmony_ci nlmsg_free(rep); 57962306a36Sopenharmony_ci goto out; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci err = nlmsg_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ciout: 58462306a36Sopenharmony_ci if (sk) 58562306a36Sopenharmony_ci sock_gen_put(sk); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci return err; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_diag_dump_one_icsk); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic int inet_diag_cmd_exact(int cmd, struct sk_buff *in_skb, 59262306a36Sopenharmony_ci const struct nlmsghdr *nlh, 59362306a36Sopenharmony_ci int hdrlen, 59462306a36Sopenharmony_ci const struct inet_diag_req_v2 *req) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci const struct inet_diag_handler *handler; 59762306a36Sopenharmony_ci struct inet_diag_dump_data dump_data; 59862306a36Sopenharmony_ci int err, protocol; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci memset(&dump_data, 0, sizeof(dump_data)); 60162306a36Sopenharmony_ci err = inet_diag_parse_attrs(nlh, hdrlen, dump_data.req_nlas); 60262306a36Sopenharmony_ci if (err) 60362306a36Sopenharmony_ci return err; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci protocol = inet_diag_get_protocol(req, &dump_data); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci handler = inet_diag_lock_handler(protocol); 60862306a36Sopenharmony_ci if (IS_ERR(handler)) { 60962306a36Sopenharmony_ci err = PTR_ERR(handler); 61062306a36Sopenharmony_ci } else if (cmd == SOCK_DIAG_BY_FAMILY) { 61162306a36Sopenharmony_ci struct netlink_callback cb = { 61262306a36Sopenharmony_ci .nlh = nlh, 61362306a36Sopenharmony_ci .skb = in_skb, 61462306a36Sopenharmony_ci .data = &dump_data, 61562306a36Sopenharmony_ci }; 61662306a36Sopenharmony_ci err = handler->dump_one(&cb, req); 61762306a36Sopenharmony_ci } else if (cmd == SOCK_DESTROY && handler->destroy) { 61862306a36Sopenharmony_ci err = handler->destroy(in_skb, req); 61962306a36Sopenharmony_ci } else { 62062306a36Sopenharmony_ci err = -EOPNOTSUPP; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci inet_diag_unlock_handler(handler); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci return err; 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic int bitstring_match(const __be32 *a1, const __be32 *a2, int bits) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci int words = bits >> 5; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci bits &= 0x1f; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (words) { 63462306a36Sopenharmony_ci if (memcmp(a1, a2, words << 2)) 63562306a36Sopenharmony_ci return 0; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci if (bits) { 63862306a36Sopenharmony_ci __be32 w1, w2; 63962306a36Sopenharmony_ci __be32 mask; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci w1 = a1[words]; 64262306a36Sopenharmony_ci w2 = a2[words]; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci mask = htonl((0xffffffff) << (32 - bits)); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if ((w1 ^ w2) & mask) 64762306a36Sopenharmony_ci return 0; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci return 1; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic int inet_diag_bc_run(const struct nlattr *_bc, 65462306a36Sopenharmony_ci const struct inet_diag_entry *entry) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci const void *bc = nla_data(_bc); 65762306a36Sopenharmony_ci int len = nla_len(_bc); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci while (len > 0) { 66062306a36Sopenharmony_ci int yes = 1; 66162306a36Sopenharmony_ci const struct inet_diag_bc_op *op = bc; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci switch (op->code) { 66462306a36Sopenharmony_ci case INET_DIAG_BC_NOP: 66562306a36Sopenharmony_ci break; 66662306a36Sopenharmony_ci case INET_DIAG_BC_JMP: 66762306a36Sopenharmony_ci yes = 0; 66862306a36Sopenharmony_ci break; 66962306a36Sopenharmony_ci case INET_DIAG_BC_S_EQ: 67062306a36Sopenharmony_ci yes = entry->sport == op[1].no; 67162306a36Sopenharmony_ci break; 67262306a36Sopenharmony_ci case INET_DIAG_BC_S_GE: 67362306a36Sopenharmony_ci yes = entry->sport >= op[1].no; 67462306a36Sopenharmony_ci break; 67562306a36Sopenharmony_ci case INET_DIAG_BC_S_LE: 67662306a36Sopenharmony_ci yes = entry->sport <= op[1].no; 67762306a36Sopenharmony_ci break; 67862306a36Sopenharmony_ci case INET_DIAG_BC_D_EQ: 67962306a36Sopenharmony_ci yes = entry->dport == op[1].no; 68062306a36Sopenharmony_ci break; 68162306a36Sopenharmony_ci case INET_DIAG_BC_D_GE: 68262306a36Sopenharmony_ci yes = entry->dport >= op[1].no; 68362306a36Sopenharmony_ci break; 68462306a36Sopenharmony_ci case INET_DIAG_BC_D_LE: 68562306a36Sopenharmony_ci yes = entry->dport <= op[1].no; 68662306a36Sopenharmony_ci break; 68762306a36Sopenharmony_ci case INET_DIAG_BC_AUTO: 68862306a36Sopenharmony_ci yes = !(entry->userlocks & SOCK_BINDPORT_LOCK); 68962306a36Sopenharmony_ci break; 69062306a36Sopenharmony_ci case INET_DIAG_BC_S_COND: 69162306a36Sopenharmony_ci case INET_DIAG_BC_D_COND: { 69262306a36Sopenharmony_ci const struct inet_diag_hostcond *cond; 69362306a36Sopenharmony_ci const __be32 *addr; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci cond = (const struct inet_diag_hostcond *)(op + 1); 69662306a36Sopenharmony_ci if (cond->port != -1 && 69762306a36Sopenharmony_ci cond->port != (op->code == INET_DIAG_BC_S_COND ? 69862306a36Sopenharmony_ci entry->sport : entry->dport)) { 69962306a36Sopenharmony_ci yes = 0; 70062306a36Sopenharmony_ci break; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (op->code == INET_DIAG_BC_S_COND) 70462306a36Sopenharmony_ci addr = entry->saddr; 70562306a36Sopenharmony_ci else 70662306a36Sopenharmony_ci addr = entry->daddr; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (cond->family != AF_UNSPEC && 70962306a36Sopenharmony_ci cond->family != entry->family) { 71062306a36Sopenharmony_ci if (entry->family == AF_INET6 && 71162306a36Sopenharmony_ci cond->family == AF_INET) { 71262306a36Sopenharmony_ci if (addr[0] == 0 && addr[1] == 0 && 71362306a36Sopenharmony_ci addr[2] == htonl(0xffff) && 71462306a36Sopenharmony_ci bitstring_match(addr + 3, 71562306a36Sopenharmony_ci cond->addr, 71662306a36Sopenharmony_ci cond->prefix_len)) 71762306a36Sopenharmony_ci break; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci yes = 0; 72062306a36Sopenharmony_ci break; 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (cond->prefix_len == 0) 72462306a36Sopenharmony_ci break; 72562306a36Sopenharmony_ci if (bitstring_match(addr, cond->addr, 72662306a36Sopenharmony_ci cond->prefix_len)) 72762306a36Sopenharmony_ci break; 72862306a36Sopenharmony_ci yes = 0; 72962306a36Sopenharmony_ci break; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci case INET_DIAG_BC_DEV_COND: { 73262306a36Sopenharmony_ci u32 ifindex; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci ifindex = *((const u32 *)(op + 1)); 73562306a36Sopenharmony_ci if (ifindex != entry->ifindex) 73662306a36Sopenharmony_ci yes = 0; 73762306a36Sopenharmony_ci break; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci case INET_DIAG_BC_MARK_COND: { 74062306a36Sopenharmony_ci struct inet_diag_markcond *cond; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci cond = (struct inet_diag_markcond *)(op + 1); 74362306a36Sopenharmony_ci if ((entry->mark & cond->mask) != cond->mark) 74462306a36Sopenharmony_ci yes = 0; 74562306a36Sopenharmony_ci break; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci#ifdef CONFIG_SOCK_CGROUP_DATA 74862306a36Sopenharmony_ci case INET_DIAG_BC_CGROUP_COND: { 74962306a36Sopenharmony_ci u64 cgroup_id; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci cgroup_id = get_unaligned((const u64 *)(op + 1)); 75262306a36Sopenharmony_ci if (cgroup_id != entry->cgroup_id) 75362306a36Sopenharmony_ci yes = 0; 75462306a36Sopenharmony_ci break; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci#endif 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci if (yes) { 76062306a36Sopenharmony_ci len -= op->yes; 76162306a36Sopenharmony_ci bc += op->yes; 76262306a36Sopenharmony_ci } else { 76362306a36Sopenharmony_ci len -= op->no; 76462306a36Sopenharmony_ci bc += op->no; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci return len == 0; 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci/* This helper is available for all sockets (ESTABLISH, TIMEWAIT, SYN_RECV) 77162306a36Sopenharmony_ci */ 77262306a36Sopenharmony_cistatic void entry_fill_addrs(struct inet_diag_entry *entry, 77362306a36Sopenharmony_ci const struct sock *sk) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 77662306a36Sopenharmony_ci if (sk->sk_family == AF_INET6) { 77762306a36Sopenharmony_ci entry->saddr = sk->sk_v6_rcv_saddr.s6_addr32; 77862306a36Sopenharmony_ci entry->daddr = sk->sk_v6_daddr.s6_addr32; 77962306a36Sopenharmony_ci } else 78062306a36Sopenharmony_ci#endif 78162306a36Sopenharmony_ci { 78262306a36Sopenharmony_ci entry->saddr = &sk->sk_rcv_saddr; 78362306a36Sopenharmony_ci entry->daddr = &sk->sk_daddr; 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ciint inet_diag_bc_sk(const struct nlattr *bc, struct sock *sk) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 79062306a36Sopenharmony_ci struct inet_diag_entry entry; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci if (!bc) 79362306a36Sopenharmony_ci return 1; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci entry.family = sk->sk_family; 79662306a36Sopenharmony_ci entry_fill_addrs(&entry, sk); 79762306a36Sopenharmony_ci entry.sport = inet->inet_num; 79862306a36Sopenharmony_ci entry.dport = ntohs(inet->inet_dport); 79962306a36Sopenharmony_ci entry.ifindex = sk->sk_bound_dev_if; 80062306a36Sopenharmony_ci entry.userlocks = sk_fullsock(sk) ? sk->sk_userlocks : 0; 80162306a36Sopenharmony_ci if (sk_fullsock(sk)) 80262306a36Sopenharmony_ci entry.mark = READ_ONCE(sk->sk_mark); 80362306a36Sopenharmony_ci else if (sk->sk_state == TCP_NEW_SYN_RECV) 80462306a36Sopenharmony_ci entry.mark = inet_rsk(inet_reqsk(sk))->ir_mark; 80562306a36Sopenharmony_ci else if (sk->sk_state == TCP_TIME_WAIT) 80662306a36Sopenharmony_ci entry.mark = inet_twsk(sk)->tw_mark; 80762306a36Sopenharmony_ci else 80862306a36Sopenharmony_ci entry.mark = 0; 80962306a36Sopenharmony_ci#ifdef CONFIG_SOCK_CGROUP_DATA 81062306a36Sopenharmony_ci entry.cgroup_id = sk_fullsock(sk) ? 81162306a36Sopenharmony_ci cgroup_id(sock_cgroup_ptr(&sk->sk_cgrp_data)) : 0; 81262306a36Sopenharmony_ci#endif 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci return inet_diag_bc_run(bc, &entry); 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_diag_bc_sk); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cistatic int valid_cc(const void *bc, int len, int cc) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci while (len >= 0) { 82162306a36Sopenharmony_ci const struct inet_diag_bc_op *op = bc; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci if (cc > len) 82462306a36Sopenharmony_ci return 0; 82562306a36Sopenharmony_ci if (cc == len) 82662306a36Sopenharmony_ci return 1; 82762306a36Sopenharmony_ci if (op->yes < 4 || op->yes & 3) 82862306a36Sopenharmony_ci return 0; 82962306a36Sopenharmony_ci len -= op->yes; 83062306a36Sopenharmony_ci bc += op->yes; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci return 0; 83362306a36Sopenharmony_ci} 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci/* data is u32 ifindex */ 83662306a36Sopenharmony_cistatic bool valid_devcond(const struct inet_diag_bc_op *op, int len, 83762306a36Sopenharmony_ci int *min_len) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci /* Check ifindex space. */ 84062306a36Sopenharmony_ci *min_len += sizeof(u32); 84162306a36Sopenharmony_ci if (len < *min_len) 84262306a36Sopenharmony_ci return false; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci return true; 84562306a36Sopenharmony_ci} 84662306a36Sopenharmony_ci/* Validate an inet_diag_hostcond. */ 84762306a36Sopenharmony_cistatic bool valid_hostcond(const struct inet_diag_bc_op *op, int len, 84862306a36Sopenharmony_ci int *min_len) 84962306a36Sopenharmony_ci{ 85062306a36Sopenharmony_ci struct inet_diag_hostcond *cond; 85162306a36Sopenharmony_ci int addr_len; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci /* Check hostcond space. */ 85462306a36Sopenharmony_ci *min_len += sizeof(struct inet_diag_hostcond); 85562306a36Sopenharmony_ci if (len < *min_len) 85662306a36Sopenharmony_ci return false; 85762306a36Sopenharmony_ci cond = (struct inet_diag_hostcond *)(op + 1); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci /* Check address family and address length. */ 86062306a36Sopenharmony_ci switch (cond->family) { 86162306a36Sopenharmony_ci case AF_UNSPEC: 86262306a36Sopenharmony_ci addr_len = 0; 86362306a36Sopenharmony_ci break; 86462306a36Sopenharmony_ci case AF_INET: 86562306a36Sopenharmony_ci addr_len = sizeof(struct in_addr); 86662306a36Sopenharmony_ci break; 86762306a36Sopenharmony_ci case AF_INET6: 86862306a36Sopenharmony_ci addr_len = sizeof(struct in6_addr); 86962306a36Sopenharmony_ci break; 87062306a36Sopenharmony_ci default: 87162306a36Sopenharmony_ci return false; 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci *min_len += addr_len; 87462306a36Sopenharmony_ci if (len < *min_len) 87562306a36Sopenharmony_ci return false; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci /* Check prefix length (in bits) vs address length (in bytes). */ 87862306a36Sopenharmony_ci if (cond->prefix_len > 8 * addr_len) 87962306a36Sopenharmony_ci return false; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci return true; 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci/* Validate a port comparison operator. */ 88562306a36Sopenharmony_cistatic bool valid_port_comparison(const struct inet_diag_bc_op *op, 88662306a36Sopenharmony_ci int len, int *min_len) 88762306a36Sopenharmony_ci{ 88862306a36Sopenharmony_ci /* Port comparisons put the port in a follow-on inet_diag_bc_op. */ 88962306a36Sopenharmony_ci *min_len += sizeof(struct inet_diag_bc_op); 89062306a36Sopenharmony_ci if (len < *min_len) 89162306a36Sopenharmony_ci return false; 89262306a36Sopenharmony_ci return true; 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic bool valid_markcond(const struct inet_diag_bc_op *op, int len, 89662306a36Sopenharmony_ci int *min_len) 89762306a36Sopenharmony_ci{ 89862306a36Sopenharmony_ci *min_len += sizeof(struct inet_diag_markcond); 89962306a36Sopenharmony_ci return len >= *min_len; 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci#ifdef CONFIG_SOCK_CGROUP_DATA 90362306a36Sopenharmony_cistatic bool valid_cgroupcond(const struct inet_diag_bc_op *op, int len, 90462306a36Sopenharmony_ci int *min_len) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci *min_len += sizeof(u64); 90762306a36Sopenharmony_ci return len >= *min_len; 90862306a36Sopenharmony_ci} 90962306a36Sopenharmony_ci#endif 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic int inet_diag_bc_audit(const struct nlattr *attr, 91262306a36Sopenharmony_ci const struct sk_buff *skb) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci bool net_admin = netlink_net_capable(skb, CAP_NET_ADMIN); 91562306a36Sopenharmony_ci const void *bytecode, *bc; 91662306a36Sopenharmony_ci int bytecode_len, len; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (!attr || nla_len(attr) < sizeof(struct inet_diag_bc_op)) 91962306a36Sopenharmony_ci return -EINVAL; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci bytecode = bc = nla_data(attr); 92262306a36Sopenharmony_ci len = bytecode_len = nla_len(attr); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci while (len > 0) { 92562306a36Sopenharmony_ci int min_len = sizeof(struct inet_diag_bc_op); 92662306a36Sopenharmony_ci const struct inet_diag_bc_op *op = bc; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci switch (op->code) { 92962306a36Sopenharmony_ci case INET_DIAG_BC_S_COND: 93062306a36Sopenharmony_ci case INET_DIAG_BC_D_COND: 93162306a36Sopenharmony_ci if (!valid_hostcond(bc, len, &min_len)) 93262306a36Sopenharmony_ci return -EINVAL; 93362306a36Sopenharmony_ci break; 93462306a36Sopenharmony_ci case INET_DIAG_BC_DEV_COND: 93562306a36Sopenharmony_ci if (!valid_devcond(bc, len, &min_len)) 93662306a36Sopenharmony_ci return -EINVAL; 93762306a36Sopenharmony_ci break; 93862306a36Sopenharmony_ci case INET_DIAG_BC_S_EQ: 93962306a36Sopenharmony_ci case INET_DIAG_BC_S_GE: 94062306a36Sopenharmony_ci case INET_DIAG_BC_S_LE: 94162306a36Sopenharmony_ci case INET_DIAG_BC_D_EQ: 94262306a36Sopenharmony_ci case INET_DIAG_BC_D_GE: 94362306a36Sopenharmony_ci case INET_DIAG_BC_D_LE: 94462306a36Sopenharmony_ci if (!valid_port_comparison(bc, len, &min_len)) 94562306a36Sopenharmony_ci return -EINVAL; 94662306a36Sopenharmony_ci break; 94762306a36Sopenharmony_ci case INET_DIAG_BC_MARK_COND: 94862306a36Sopenharmony_ci if (!net_admin) 94962306a36Sopenharmony_ci return -EPERM; 95062306a36Sopenharmony_ci if (!valid_markcond(bc, len, &min_len)) 95162306a36Sopenharmony_ci return -EINVAL; 95262306a36Sopenharmony_ci break; 95362306a36Sopenharmony_ci#ifdef CONFIG_SOCK_CGROUP_DATA 95462306a36Sopenharmony_ci case INET_DIAG_BC_CGROUP_COND: 95562306a36Sopenharmony_ci if (!valid_cgroupcond(bc, len, &min_len)) 95662306a36Sopenharmony_ci return -EINVAL; 95762306a36Sopenharmony_ci break; 95862306a36Sopenharmony_ci#endif 95962306a36Sopenharmony_ci case INET_DIAG_BC_AUTO: 96062306a36Sopenharmony_ci case INET_DIAG_BC_JMP: 96162306a36Sopenharmony_ci case INET_DIAG_BC_NOP: 96262306a36Sopenharmony_ci break; 96362306a36Sopenharmony_ci default: 96462306a36Sopenharmony_ci return -EINVAL; 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci if (op->code != INET_DIAG_BC_NOP) { 96862306a36Sopenharmony_ci if (op->no < min_len || op->no > len + 4 || op->no & 3) 96962306a36Sopenharmony_ci return -EINVAL; 97062306a36Sopenharmony_ci if (op->no < len && 97162306a36Sopenharmony_ci !valid_cc(bytecode, bytecode_len, len - op->no)) 97262306a36Sopenharmony_ci return -EINVAL; 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci if (op->yes < min_len || op->yes > len + 4 || op->yes & 3) 97662306a36Sopenharmony_ci return -EINVAL; 97762306a36Sopenharmony_ci bc += op->yes; 97862306a36Sopenharmony_ci len -= op->yes; 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci return len == 0 ? 0 : -EINVAL; 98162306a36Sopenharmony_ci} 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_cistatic void twsk_build_assert(void) 98462306a36Sopenharmony_ci{ 98562306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_family) != 98662306a36Sopenharmony_ci offsetof(struct sock, sk_family)); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_num) != 98962306a36Sopenharmony_ci offsetof(struct inet_sock, inet_num)); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_dport) != 99262306a36Sopenharmony_ci offsetof(struct inet_sock, inet_dport)); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_rcv_saddr) != 99562306a36Sopenharmony_ci offsetof(struct inet_sock, inet_rcv_saddr)); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_daddr) != 99862306a36Sopenharmony_ci offsetof(struct inet_sock, inet_daddr)); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 100162306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_v6_rcv_saddr) != 100262306a36Sopenharmony_ci offsetof(struct sock, sk_v6_rcv_saddr)); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_v6_daddr) != 100562306a36Sopenharmony_ci offsetof(struct sock, sk_v6_daddr)); 100662306a36Sopenharmony_ci#endif 100762306a36Sopenharmony_ci} 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_civoid inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, 101062306a36Sopenharmony_ci struct netlink_callback *cb, 101162306a36Sopenharmony_ci const struct inet_diag_req_v2 *r) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci bool net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN); 101462306a36Sopenharmony_ci struct inet_diag_dump_data *cb_data = cb->data; 101562306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 101662306a36Sopenharmony_ci u32 idiag_states = r->idiag_states; 101762306a36Sopenharmony_ci int i, num, s_i, s_num; 101862306a36Sopenharmony_ci struct nlattr *bc; 101962306a36Sopenharmony_ci struct sock *sk; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci bc = cb_data->inet_diag_nla_bc; 102262306a36Sopenharmony_ci if (idiag_states & TCPF_SYN_RECV) 102362306a36Sopenharmony_ci idiag_states |= TCPF_NEW_SYN_RECV; 102462306a36Sopenharmony_ci s_i = cb->args[1]; 102562306a36Sopenharmony_ci s_num = num = cb->args[2]; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci if (cb->args[0] == 0) { 102862306a36Sopenharmony_ci if (!(idiag_states & TCPF_LISTEN) || r->id.idiag_dport) 102962306a36Sopenharmony_ci goto skip_listen_ht; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci for (i = s_i; i <= hashinfo->lhash2_mask; i++) { 103262306a36Sopenharmony_ci struct inet_listen_hashbucket *ilb; 103362306a36Sopenharmony_ci struct hlist_nulls_node *node; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci num = 0; 103662306a36Sopenharmony_ci ilb = &hashinfo->lhash2[i]; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci spin_lock(&ilb->lock); 103962306a36Sopenharmony_ci sk_nulls_for_each(sk, node, &ilb->nulls_head) { 104062306a36Sopenharmony_ci struct inet_sock *inet = inet_sk(sk); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci if (!net_eq(sock_net(sk), net)) 104362306a36Sopenharmony_ci continue; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci if (num < s_num) { 104662306a36Sopenharmony_ci num++; 104762306a36Sopenharmony_ci continue; 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci if (r->sdiag_family != AF_UNSPEC && 105162306a36Sopenharmony_ci sk->sk_family != r->sdiag_family) 105262306a36Sopenharmony_ci goto next_listen; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci if (r->id.idiag_sport != inet->inet_sport && 105562306a36Sopenharmony_ci r->id.idiag_sport) 105662306a36Sopenharmony_ci goto next_listen; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci if (!inet_diag_bc_sk(bc, sk)) 105962306a36Sopenharmony_ci goto next_listen; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci if (inet_sk_diag_fill(sk, inet_csk(sk), skb, 106262306a36Sopenharmony_ci cb, r, NLM_F_MULTI, 106362306a36Sopenharmony_ci net_admin) < 0) { 106462306a36Sopenharmony_ci spin_unlock(&ilb->lock); 106562306a36Sopenharmony_ci goto done; 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_cinext_listen: 106962306a36Sopenharmony_ci ++num; 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci spin_unlock(&ilb->lock); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci s_num = 0; 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ciskip_listen_ht: 107662306a36Sopenharmony_ci cb->args[0] = 1; 107762306a36Sopenharmony_ci s_i = num = s_num = 0; 107862306a36Sopenharmony_ci } 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci if (!(idiag_states & ~TCPF_LISTEN)) 108162306a36Sopenharmony_ci goto out; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci#define SKARR_SZ 16 108462306a36Sopenharmony_ci for (i = s_i; i <= hashinfo->ehash_mask; i++) { 108562306a36Sopenharmony_ci struct inet_ehash_bucket *head = &hashinfo->ehash[i]; 108662306a36Sopenharmony_ci spinlock_t *lock = inet_ehash_lockp(hashinfo, i); 108762306a36Sopenharmony_ci struct hlist_nulls_node *node; 108862306a36Sopenharmony_ci struct sock *sk_arr[SKARR_SZ]; 108962306a36Sopenharmony_ci int num_arr[SKARR_SZ]; 109062306a36Sopenharmony_ci int idx, accum, res; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci if (hlist_nulls_empty(&head->chain)) 109362306a36Sopenharmony_ci continue; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci if (i > s_i) 109662306a36Sopenharmony_ci s_num = 0; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_cinext_chunk: 109962306a36Sopenharmony_ci num = 0; 110062306a36Sopenharmony_ci accum = 0; 110162306a36Sopenharmony_ci spin_lock_bh(lock); 110262306a36Sopenharmony_ci sk_nulls_for_each(sk, node, &head->chain) { 110362306a36Sopenharmony_ci int state; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci if (!net_eq(sock_net(sk), net)) 110662306a36Sopenharmony_ci continue; 110762306a36Sopenharmony_ci if (num < s_num) 110862306a36Sopenharmony_ci goto next_normal; 110962306a36Sopenharmony_ci state = (sk->sk_state == TCP_TIME_WAIT) ? 111062306a36Sopenharmony_ci inet_twsk(sk)->tw_substate : sk->sk_state; 111162306a36Sopenharmony_ci if (!(idiag_states & (1 << state))) 111262306a36Sopenharmony_ci goto next_normal; 111362306a36Sopenharmony_ci if (r->sdiag_family != AF_UNSPEC && 111462306a36Sopenharmony_ci sk->sk_family != r->sdiag_family) 111562306a36Sopenharmony_ci goto next_normal; 111662306a36Sopenharmony_ci if (r->id.idiag_sport != htons(sk->sk_num) && 111762306a36Sopenharmony_ci r->id.idiag_sport) 111862306a36Sopenharmony_ci goto next_normal; 111962306a36Sopenharmony_ci if (r->id.idiag_dport != sk->sk_dport && 112062306a36Sopenharmony_ci r->id.idiag_dport) 112162306a36Sopenharmony_ci goto next_normal; 112262306a36Sopenharmony_ci twsk_build_assert(); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci if (!inet_diag_bc_sk(bc, sk)) 112562306a36Sopenharmony_ci goto next_normal; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci if (!refcount_inc_not_zero(&sk->sk_refcnt)) 112862306a36Sopenharmony_ci goto next_normal; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci num_arr[accum] = num; 113162306a36Sopenharmony_ci sk_arr[accum] = sk; 113262306a36Sopenharmony_ci if (++accum == SKARR_SZ) 113362306a36Sopenharmony_ci break; 113462306a36Sopenharmony_cinext_normal: 113562306a36Sopenharmony_ci ++num; 113662306a36Sopenharmony_ci } 113762306a36Sopenharmony_ci spin_unlock_bh(lock); 113862306a36Sopenharmony_ci res = 0; 113962306a36Sopenharmony_ci for (idx = 0; idx < accum; idx++) { 114062306a36Sopenharmony_ci if (res >= 0) { 114162306a36Sopenharmony_ci res = sk_diag_fill(sk_arr[idx], skb, cb, r, 114262306a36Sopenharmony_ci NLM_F_MULTI, net_admin); 114362306a36Sopenharmony_ci if (res < 0) 114462306a36Sopenharmony_ci num = num_arr[idx]; 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci sock_gen_put(sk_arr[idx]); 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci if (res < 0) 114962306a36Sopenharmony_ci break; 115062306a36Sopenharmony_ci cond_resched(); 115162306a36Sopenharmony_ci if (accum == SKARR_SZ) { 115262306a36Sopenharmony_ci s_num = num + 1; 115362306a36Sopenharmony_ci goto next_chunk; 115462306a36Sopenharmony_ci } 115562306a36Sopenharmony_ci } 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_cidone: 115862306a36Sopenharmony_ci cb->args[1] = i; 115962306a36Sopenharmony_ci cb->args[2] = num; 116062306a36Sopenharmony_ciout: 116162306a36Sopenharmony_ci ; 116262306a36Sopenharmony_ci} 116362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_diag_dump_icsk); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_cistatic int __inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, 116662306a36Sopenharmony_ci const struct inet_diag_req_v2 *r) 116762306a36Sopenharmony_ci{ 116862306a36Sopenharmony_ci struct inet_diag_dump_data *cb_data = cb->data; 116962306a36Sopenharmony_ci const struct inet_diag_handler *handler; 117062306a36Sopenharmony_ci u32 prev_min_dump_alloc; 117162306a36Sopenharmony_ci int protocol, err = 0; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci protocol = inet_diag_get_protocol(r, cb_data); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ciagain: 117662306a36Sopenharmony_ci prev_min_dump_alloc = cb->min_dump_alloc; 117762306a36Sopenharmony_ci handler = inet_diag_lock_handler(protocol); 117862306a36Sopenharmony_ci if (!IS_ERR(handler)) 117962306a36Sopenharmony_ci handler->dump(skb, cb, r); 118062306a36Sopenharmony_ci else 118162306a36Sopenharmony_ci err = PTR_ERR(handler); 118262306a36Sopenharmony_ci inet_diag_unlock_handler(handler); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci /* The skb is not large enough to fit one sk info and 118562306a36Sopenharmony_ci * inet_sk_diag_fill() has requested for a larger skb. 118662306a36Sopenharmony_ci */ 118762306a36Sopenharmony_ci if (!skb->len && cb->min_dump_alloc > prev_min_dump_alloc) { 118862306a36Sopenharmony_ci err = pskb_expand_head(skb, 0, cb->min_dump_alloc, GFP_KERNEL); 118962306a36Sopenharmony_ci if (!err) 119062306a36Sopenharmony_ci goto again; 119162306a36Sopenharmony_ci } 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci return err ? : skb->len; 119462306a36Sopenharmony_ci} 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_cistatic int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) 119762306a36Sopenharmony_ci{ 119862306a36Sopenharmony_ci return __inet_diag_dump(skb, cb, nlmsg_data(cb->nlh)); 119962306a36Sopenharmony_ci} 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_cistatic int __inet_diag_dump_start(struct netlink_callback *cb, int hdrlen) 120262306a36Sopenharmony_ci{ 120362306a36Sopenharmony_ci const struct nlmsghdr *nlh = cb->nlh; 120462306a36Sopenharmony_ci struct inet_diag_dump_data *cb_data; 120562306a36Sopenharmony_ci struct sk_buff *skb = cb->skb; 120662306a36Sopenharmony_ci struct nlattr *nla; 120762306a36Sopenharmony_ci int err; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci cb_data = kzalloc(sizeof(*cb_data), GFP_KERNEL); 121062306a36Sopenharmony_ci if (!cb_data) 121162306a36Sopenharmony_ci return -ENOMEM; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci err = inet_diag_parse_attrs(nlh, hdrlen, cb_data->req_nlas); 121462306a36Sopenharmony_ci if (err) { 121562306a36Sopenharmony_ci kfree(cb_data); 121662306a36Sopenharmony_ci return err; 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci nla = cb_data->inet_diag_nla_bc; 121962306a36Sopenharmony_ci if (nla) { 122062306a36Sopenharmony_ci err = inet_diag_bc_audit(nla, skb); 122162306a36Sopenharmony_ci if (err) { 122262306a36Sopenharmony_ci kfree(cb_data); 122362306a36Sopenharmony_ci return err; 122462306a36Sopenharmony_ci } 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci nla = cb_data->inet_diag_nla_bpf_stgs; 122862306a36Sopenharmony_ci if (nla) { 122962306a36Sopenharmony_ci struct bpf_sk_storage_diag *bpf_stg_diag; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci bpf_stg_diag = bpf_sk_storage_diag_alloc(nla); 123262306a36Sopenharmony_ci if (IS_ERR(bpf_stg_diag)) { 123362306a36Sopenharmony_ci kfree(cb_data); 123462306a36Sopenharmony_ci return PTR_ERR(bpf_stg_diag); 123562306a36Sopenharmony_ci } 123662306a36Sopenharmony_ci cb_data->bpf_stg_diag = bpf_stg_diag; 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci cb->data = cb_data; 124062306a36Sopenharmony_ci return 0; 124162306a36Sopenharmony_ci} 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_cistatic int inet_diag_dump_start(struct netlink_callback *cb) 124462306a36Sopenharmony_ci{ 124562306a36Sopenharmony_ci return __inet_diag_dump_start(cb, sizeof(struct inet_diag_req_v2)); 124662306a36Sopenharmony_ci} 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_cistatic int inet_diag_dump_start_compat(struct netlink_callback *cb) 124962306a36Sopenharmony_ci{ 125062306a36Sopenharmony_ci return __inet_diag_dump_start(cb, sizeof(struct inet_diag_req)); 125162306a36Sopenharmony_ci} 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_cistatic int inet_diag_dump_done(struct netlink_callback *cb) 125462306a36Sopenharmony_ci{ 125562306a36Sopenharmony_ci struct inet_diag_dump_data *cb_data = cb->data; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci bpf_sk_storage_diag_free(cb_data->bpf_stg_diag); 125862306a36Sopenharmony_ci kfree(cb->data); 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci return 0; 126162306a36Sopenharmony_ci} 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_cistatic int inet_diag_type2proto(int type) 126462306a36Sopenharmony_ci{ 126562306a36Sopenharmony_ci switch (type) { 126662306a36Sopenharmony_ci case TCPDIAG_GETSOCK: 126762306a36Sopenharmony_ci return IPPROTO_TCP; 126862306a36Sopenharmony_ci case DCCPDIAG_GETSOCK: 126962306a36Sopenharmony_ci return IPPROTO_DCCP; 127062306a36Sopenharmony_ci default: 127162306a36Sopenharmony_ci return 0; 127262306a36Sopenharmony_ci } 127362306a36Sopenharmony_ci} 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_cistatic int inet_diag_dump_compat(struct sk_buff *skb, 127662306a36Sopenharmony_ci struct netlink_callback *cb) 127762306a36Sopenharmony_ci{ 127862306a36Sopenharmony_ci struct inet_diag_req *rc = nlmsg_data(cb->nlh); 127962306a36Sopenharmony_ci struct inet_diag_req_v2 req; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci req.sdiag_family = AF_UNSPEC; /* compatibility */ 128262306a36Sopenharmony_ci req.sdiag_protocol = inet_diag_type2proto(cb->nlh->nlmsg_type); 128362306a36Sopenharmony_ci req.idiag_ext = rc->idiag_ext; 128462306a36Sopenharmony_ci req.idiag_states = rc->idiag_states; 128562306a36Sopenharmony_ci req.id = rc->id; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci return __inet_diag_dump(skb, cb, &req); 128862306a36Sopenharmony_ci} 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_cistatic int inet_diag_get_exact_compat(struct sk_buff *in_skb, 129162306a36Sopenharmony_ci const struct nlmsghdr *nlh) 129262306a36Sopenharmony_ci{ 129362306a36Sopenharmony_ci struct inet_diag_req *rc = nlmsg_data(nlh); 129462306a36Sopenharmony_ci struct inet_diag_req_v2 req; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci req.sdiag_family = rc->idiag_family; 129762306a36Sopenharmony_ci req.sdiag_protocol = inet_diag_type2proto(nlh->nlmsg_type); 129862306a36Sopenharmony_ci req.idiag_ext = rc->idiag_ext; 129962306a36Sopenharmony_ci req.idiag_states = rc->idiag_states; 130062306a36Sopenharmony_ci req.id = rc->id; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci return inet_diag_cmd_exact(SOCK_DIAG_BY_FAMILY, in_skb, nlh, 130362306a36Sopenharmony_ci sizeof(struct inet_diag_req), &req); 130462306a36Sopenharmony_ci} 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_cistatic int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh) 130762306a36Sopenharmony_ci{ 130862306a36Sopenharmony_ci int hdrlen = sizeof(struct inet_diag_req); 130962306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX || 131262306a36Sopenharmony_ci nlmsg_len(nlh) < hdrlen) 131362306a36Sopenharmony_ci return -EINVAL; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci if (nlh->nlmsg_flags & NLM_F_DUMP) { 131662306a36Sopenharmony_ci struct netlink_dump_control c = { 131762306a36Sopenharmony_ci .start = inet_diag_dump_start_compat, 131862306a36Sopenharmony_ci .done = inet_diag_dump_done, 131962306a36Sopenharmony_ci .dump = inet_diag_dump_compat, 132062306a36Sopenharmony_ci }; 132162306a36Sopenharmony_ci return netlink_dump_start(net->diag_nlsk, skb, nlh, &c); 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci return inet_diag_get_exact_compat(skb, nlh); 132562306a36Sopenharmony_ci} 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_cistatic int inet_diag_handler_cmd(struct sk_buff *skb, struct nlmsghdr *h) 132862306a36Sopenharmony_ci{ 132962306a36Sopenharmony_ci int hdrlen = sizeof(struct inet_diag_req_v2); 133062306a36Sopenharmony_ci struct net *net = sock_net(skb->sk); 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci if (nlmsg_len(h) < hdrlen) 133362306a36Sopenharmony_ci return -EINVAL; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci if (h->nlmsg_type == SOCK_DIAG_BY_FAMILY && 133662306a36Sopenharmony_ci h->nlmsg_flags & NLM_F_DUMP) { 133762306a36Sopenharmony_ci struct netlink_dump_control c = { 133862306a36Sopenharmony_ci .start = inet_diag_dump_start, 133962306a36Sopenharmony_ci .done = inet_diag_dump_done, 134062306a36Sopenharmony_ci .dump = inet_diag_dump, 134162306a36Sopenharmony_ci }; 134262306a36Sopenharmony_ci return netlink_dump_start(net->diag_nlsk, skb, h, &c); 134362306a36Sopenharmony_ci } 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci return inet_diag_cmd_exact(h->nlmsg_type, skb, h, hdrlen, 134662306a36Sopenharmony_ci nlmsg_data(h)); 134762306a36Sopenharmony_ci} 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_cistatic 135062306a36Sopenharmony_ciint inet_diag_handler_get_info(struct sk_buff *skb, struct sock *sk) 135162306a36Sopenharmony_ci{ 135262306a36Sopenharmony_ci const struct inet_diag_handler *handler; 135362306a36Sopenharmony_ci struct nlmsghdr *nlh; 135462306a36Sopenharmony_ci struct nlattr *attr; 135562306a36Sopenharmony_ci struct inet_diag_msg *r; 135662306a36Sopenharmony_ci void *info = NULL; 135762306a36Sopenharmony_ci int err = 0; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci nlh = nlmsg_put(skb, 0, 0, SOCK_DIAG_BY_FAMILY, sizeof(*r), 0); 136062306a36Sopenharmony_ci if (!nlh) 136162306a36Sopenharmony_ci return -ENOMEM; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci r = nlmsg_data(nlh); 136462306a36Sopenharmony_ci memset(r, 0, sizeof(*r)); 136562306a36Sopenharmony_ci inet_diag_msg_common_fill(r, sk); 136662306a36Sopenharmony_ci if (sk->sk_type == SOCK_DGRAM || sk->sk_type == SOCK_STREAM) 136762306a36Sopenharmony_ci r->id.idiag_sport = inet_sk(sk)->inet_sport; 136862306a36Sopenharmony_ci r->idiag_state = sk->sk_state; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci if ((err = nla_put_u8(skb, INET_DIAG_PROTOCOL, sk->sk_protocol))) { 137162306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 137262306a36Sopenharmony_ci return err; 137362306a36Sopenharmony_ci } 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci handler = inet_diag_lock_handler(sk->sk_protocol); 137662306a36Sopenharmony_ci if (IS_ERR(handler)) { 137762306a36Sopenharmony_ci inet_diag_unlock_handler(handler); 137862306a36Sopenharmony_ci nlmsg_cancel(skb, nlh); 137962306a36Sopenharmony_ci return PTR_ERR(handler); 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci attr = handler->idiag_info_size 138362306a36Sopenharmony_ci ? nla_reserve_64bit(skb, INET_DIAG_INFO, 138462306a36Sopenharmony_ci handler->idiag_info_size, 138562306a36Sopenharmony_ci INET_DIAG_PAD) 138662306a36Sopenharmony_ci : NULL; 138762306a36Sopenharmony_ci if (attr) 138862306a36Sopenharmony_ci info = nla_data(attr); 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci handler->idiag_get_info(sk, r, info); 139162306a36Sopenharmony_ci inet_diag_unlock_handler(handler); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci nlmsg_end(skb, nlh); 139462306a36Sopenharmony_ci return 0; 139562306a36Sopenharmony_ci} 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_cistatic const struct sock_diag_handler inet_diag_handler = { 139862306a36Sopenharmony_ci .family = AF_INET, 139962306a36Sopenharmony_ci .dump = inet_diag_handler_cmd, 140062306a36Sopenharmony_ci .get_info = inet_diag_handler_get_info, 140162306a36Sopenharmony_ci .destroy = inet_diag_handler_cmd, 140262306a36Sopenharmony_ci}; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_cistatic const struct sock_diag_handler inet6_diag_handler = { 140562306a36Sopenharmony_ci .family = AF_INET6, 140662306a36Sopenharmony_ci .dump = inet_diag_handler_cmd, 140762306a36Sopenharmony_ci .get_info = inet_diag_handler_get_info, 140862306a36Sopenharmony_ci .destroy = inet_diag_handler_cmd, 140962306a36Sopenharmony_ci}; 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ciint inet_diag_register(const struct inet_diag_handler *h) 141262306a36Sopenharmony_ci{ 141362306a36Sopenharmony_ci const __u16 type = h->idiag_type; 141462306a36Sopenharmony_ci int err = -EINVAL; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci if (type >= IPPROTO_MAX) 141762306a36Sopenharmony_ci goto out; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci mutex_lock(&inet_diag_table_mutex); 142062306a36Sopenharmony_ci err = -EEXIST; 142162306a36Sopenharmony_ci if (!inet_diag_table[type]) { 142262306a36Sopenharmony_ci WRITE_ONCE(inet_diag_table[type], h); 142362306a36Sopenharmony_ci err = 0; 142462306a36Sopenharmony_ci } 142562306a36Sopenharmony_ci mutex_unlock(&inet_diag_table_mutex); 142662306a36Sopenharmony_ciout: 142762306a36Sopenharmony_ci return err; 142862306a36Sopenharmony_ci} 142962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_diag_register); 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_civoid inet_diag_unregister(const struct inet_diag_handler *h) 143262306a36Sopenharmony_ci{ 143362306a36Sopenharmony_ci const __u16 type = h->idiag_type; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci if (type >= IPPROTO_MAX) 143662306a36Sopenharmony_ci return; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci mutex_lock(&inet_diag_table_mutex); 143962306a36Sopenharmony_ci WRITE_ONCE(inet_diag_table[type], NULL); 144062306a36Sopenharmony_ci mutex_unlock(&inet_diag_table_mutex); 144162306a36Sopenharmony_ci} 144262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(inet_diag_unregister); 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_cistatic int __init inet_diag_init(void) 144562306a36Sopenharmony_ci{ 144662306a36Sopenharmony_ci const int inet_diag_table_size = (IPPROTO_MAX * 144762306a36Sopenharmony_ci sizeof(struct inet_diag_handler *)); 144862306a36Sopenharmony_ci int err = -ENOMEM; 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci inet_diag_table = kzalloc(inet_diag_table_size, GFP_KERNEL); 145162306a36Sopenharmony_ci if (!inet_diag_table) 145262306a36Sopenharmony_ci goto out; 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci err = sock_diag_register(&inet_diag_handler); 145562306a36Sopenharmony_ci if (err) 145662306a36Sopenharmony_ci goto out_free_nl; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci err = sock_diag_register(&inet6_diag_handler); 145962306a36Sopenharmony_ci if (err) 146062306a36Sopenharmony_ci goto out_free_inet; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci sock_diag_register_inet_compat(inet_diag_rcv_msg_compat); 146362306a36Sopenharmony_ciout: 146462306a36Sopenharmony_ci return err; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ciout_free_inet: 146762306a36Sopenharmony_ci sock_diag_unregister(&inet_diag_handler); 146862306a36Sopenharmony_ciout_free_nl: 146962306a36Sopenharmony_ci kfree(inet_diag_table); 147062306a36Sopenharmony_ci goto out; 147162306a36Sopenharmony_ci} 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_cistatic void __exit inet_diag_exit(void) 147462306a36Sopenharmony_ci{ 147562306a36Sopenharmony_ci sock_diag_unregister(&inet6_diag_handler); 147662306a36Sopenharmony_ci sock_diag_unregister(&inet_diag_handler); 147762306a36Sopenharmony_ci sock_diag_unregister_inet_compat(inet_diag_rcv_msg_compat); 147862306a36Sopenharmony_ci kfree(inet_diag_table); 147962306a36Sopenharmony_ci} 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_cimodule_init(inet_diag_init); 148262306a36Sopenharmony_cimodule_exit(inet_diag_exit); 148362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 148462306a36Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2 /* AF_INET */); 148562306a36Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 10 /* AF_INET6 */); 1486