12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2a8c2190eSArnaldo Carvalho de Melo /* 3a8c2190eSArnaldo Carvalho de Melo * inet_diag.c Module for monitoring INET transport protocols sockets. 4a8c2190eSArnaldo Carvalho de Melo * 5a8c2190eSArnaldo Carvalho de Melo * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 6a8c2190eSArnaldo Carvalho de Melo */ 7a8c2190eSArnaldo Carvalho de Melo 8172589ccSIlpo Järvinen #include <linux/kernel.h> 9a8c2190eSArnaldo Carvalho de Melo #include <linux/module.h> 10a8c2190eSArnaldo Carvalho de Melo #include <linux/types.h> 11a8c2190eSArnaldo Carvalho de Melo #include <linux/fcntl.h> 12a8c2190eSArnaldo Carvalho de Melo #include <linux/random.h> 135a0e3ad6STejun Heo #include <linux/slab.h> 14a8c2190eSArnaldo Carvalho de Melo #include <linux/cache.h> 15a8c2190eSArnaldo Carvalho de Melo #include <linux/init.h> 16a8c2190eSArnaldo Carvalho de Melo #include <linux/time.h> 17a8c2190eSArnaldo Carvalho de Melo 18a8c2190eSArnaldo Carvalho de Melo #include <net/icmp.h> 19a8c2190eSArnaldo Carvalho de Melo #include <net/tcp.h> 20a8c2190eSArnaldo Carvalho de Melo #include <net/ipv6.h> 21a8c2190eSArnaldo Carvalho de Melo #include <net/inet_common.h> 22a8c2190eSArnaldo Carvalho de Melo #include <net/inet_connection_sock.h> 23a8c2190eSArnaldo Carvalho de Melo #include <net/inet_hashtables.h> 24a8c2190eSArnaldo Carvalho de Melo #include <net/inet_timewait_sock.h> 25a8c2190eSArnaldo Carvalho de Melo #include <net/inet6_hashtables.h> 26dc5fc579SArnaldo Carvalho de Melo #include <net/netlink.h> 27a8c2190eSArnaldo Carvalho de Melo 28a8c2190eSArnaldo Carvalho de Melo #include <linux/inet.h> 29a8c2190eSArnaldo Carvalho de Melo #include <linux/stddef.h> 30a8c2190eSArnaldo Carvalho de Melo 31a8c2190eSArnaldo Carvalho de Melo #include <linux/inet_diag.h> 32d366477aSPavel Emelyanov #include <linux/sock_diag.h> 33a8c2190eSArnaldo Carvalho de Melo 34a8c2190eSArnaldo Carvalho de Melo static const struct inet_diag_handler **inet_diag_table; 35a8c2190eSArnaldo Carvalho de Melo 36a8c2190eSArnaldo Carvalho de Melo struct inet_diag_entry { 37e31c5e0eSEric Dumazet const __be32 *saddr; 38e31c5e0eSEric Dumazet const __be32 *daddr; 39a8c2190eSArnaldo Carvalho de Melo u16 sport; 40a8c2190eSArnaldo Carvalho de Melo u16 dport; 41a8c2190eSArnaldo Carvalho de Melo u16 family; 42a8c2190eSArnaldo Carvalho de Melo u16 userlocks; 43637c841dSDavid Ahern u32 ifindex; 44a52e95abSLorenzo Colitti u32 mark; 45a8c2190eSArnaldo Carvalho de Melo }; 46a8c2190eSArnaldo Carvalho de Melo 47d523a328SHerbert Xu static DEFINE_MUTEX(inet_diag_table_mutex); 48d523a328SHerbert Xu 49f13c95f0SPavel Emelyanov static const struct inet_diag_handler *inet_diag_lock_handler(int proto) 50d523a328SHerbert Xu { 51f13c95f0SPavel Emelyanov if (!inet_diag_table[proto]) 52bf2ae2e4SXin Long sock_load_diag_module(AF_INET, proto); 53d523a328SHerbert Xu 54d523a328SHerbert Xu mutex_lock(&inet_diag_table_mutex); 55f13c95f0SPavel Emelyanov if (!inet_diag_table[proto]) 56d523a328SHerbert Xu return ERR_PTR(-ENOENT); 57d523a328SHerbert Xu 58f13c95f0SPavel Emelyanov return inet_diag_table[proto]; 59d523a328SHerbert Xu } 60d523a328SHerbert Xu 61e31c5e0eSEric Dumazet static void inet_diag_unlock_handler(const struct inet_diag_handler *handler) 62d523a328SHerbert Xu { 63d523a328SHerbert Xu mutex_unlock(&inet_diag_table_mutex); 64d523a328SHerbert Xu } 65d523a328SHerbert Xu 66cb2050a7SXin Long void inet_diag_msg_common_fill(struct inet_diag_msg *r, struct sock *sk) 67a4458343SEric Dumazet { 68a4458343SEric Dumazet r->idiag_family = sk->sk_family; 69a4458343SEric Dumazet 70a4458343SEric Dumazet r->id.idiag_sport = htons(sk->sk_num); 71a4458343SEric Dumazet r->id.idiag_dport = sk->sk_dport; 72a4458343SEric Dumazet r->id.idiag_if = sk->sk_bound_dev_if; 73a4458343SEric Dumazet sock_diag_save_cookie(sk, r->id.idiag_cookie); 74a4458343SEric Dumazet 75a4458343SEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 76a4458343SEric Dumazet if (sk->sk_family == AF_INET6) { 77a4458343SEric Dumazet *(struct in6_addr *)r->id.idiag_src = sk->sk_v6_rcv_saddr; 78a4458343SEric Dumazet *(struct in6_addr *)r->id.idiag_dst = sk->sk_v6_daddr; 79a4458343SEric Dumazet } else 80a4458343SEric Dumazet #endif 81a4458343SEric Dumazet { 82a4458343SEric Dumazet memset(&r->id.idiag_src, 0, sizeof(r->id.idiag_src)); 83a4458343SEric Dumazet memset(&r->id.idiag_dst, 0, sizeof(r->id.idiag_dst)); 84a4458343SEric Dumazet 85a4458343SEric Dumazet r->id.idiag_src[0] = sk->sk_rcv_saddr; 86a4458343SEric Dumazet r->id.idiag_dst[0] = sk->sk_daddr; 87a4458343SEric Dumazet } 88a4458343SEric Dumazet } 89cb2050a7SXin Long EXPORT_SYMBOL_GPL(inet_diag_msg_common_fill); 90a4458343SEric Dumazet 91b37e8840SIvan Delalande static size_t inet_sk_attr_size(struct sock *sk, 92b37e8840SIvan Delalande const struct inet_diag_req_v2 *req, 93b37e8840SIvan Delalande bool net_admin) 94c8e2c80dSEric Dumazet { 95b37e8840SIvan Delalande const struct inet_diag_handler *handler; 96b37e8840SIvan Delalande size_t aux = 0; 97b37e8840SIvan Delalande 98b37e8840SIvan Delalande handler = inet_diag_table[req->sdiag_protocol]; 99b37e8840SIvan Delalande if (handler && handler->idiag_get_aux_size) 100b37e8840SIvan Delalande aux = handler->idiag_get_aux_size(sk, net_admin); 101b37e8840SIvan Delalande 102c8e2c80dSEric Dumazet return nla_total_size(sizeof(struct tcp_info)) 103c8e2c80dSEric Dumazet + nla_total_size(1) /* INET_DIAG_SHUTDOWN */ 104c8e2c80dSEric Dumazet + nla_total_size(1) /* INET_DIAG_TOS */ 105c8e2c80dSEric Dumazet + nla_total_size(1) /* INET_DIAG_TCLASS */ 106d545cacaSLorenzo Colitti + nla_total_size(4) /* INET_DIAG_MARK */ 1071ec17dbdSKonstantin Khlebnikov + nla_total_size(4) /* INET_DIAG_CLASS_ID */ 108c8e2c80dSEric Dumazet + nla_total_size(sizeof(struct inet_diag_meminfo)) 109c8e2c80dSEric Dumazet + nla_total_size(sizeof(struct inet_diag_msg)) 110c8e2c80dSEric Dumazet + nla_total_size(SK_MEMINFO_VARS * sizeof(u32)) 111c8e2c80dSEric Dumazet + nla_total_size(TCP_CA_NAME_MAX) 112c8e2c80dSEric Dumazet + nla_total_size(sizeof(struct tcpvegas_info)) 113b37e8840SIvan Delalande + aux 114c8e2c80dSEric Dumazet + 64; 115c8e2c80dSEric Dumazet } 116c8e2c80dSEric Dumazet 117cb2050a7SXin Long int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb, 118cb2050a7SXin Long struct inet_diag_msg *r, int ext, 119d545cacaSLorenzo Colitti struct user_namespace *user_ns, 120d545cacaSLorenzo Colitti bool net_admin) 121a8c2190eSArnaldo Carvalho de Melo { 122a8c2190eSArnaldo Carvalho de Melo const struct inet_sock *inet = inet_sk(sk); 123a8c2190eSArnaldo Carvalho de Melo 124e4e541a8SPavel Emelyanov if (nla_put_u8(skb, INET_DIAG_SHUTDOWN, sk->sk_shutdown)) 125e4e541a8SPavel Emelyanov goto errout; 126e4e541a8SPavel Emelyanov 127717b6d83SMaciej Żenczykowski /* IPv6 dual-stack sockets use inet->tos for IPv4 connections, 128717b6d83SMaciej Żenczykowski * hence this needs to be included regardless of socket family. 129717b6d83SMaciej Żenczykowski */ 130717b6d83SMaciej Żenczykowski if (ext & (1 << (INET_DIAG_TOS - 1))) 1316e277ed5SThomas Graf if (nla_put_u8(skb, INET_DIAG_TOS, inet->tos) < 0) 1326e277ed5SThomas Graf goto errout; 133717b6d83SMaciej Żenczykowski 134dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 135a8c2190eSArnaldo Carvalho de Melo if (r->idiag_family == AF_INET6) { 13606236ac3SMaciej Żenczykowski if (ext & (1 << (INET_DIAG_TCLASS - 1))) 137efe4208fSEric Dumazet if (nla_put_u8(skb, INET_DIAG_TCLASS, 138efe4208fSEric Dumazet inet6_sk(sk)->tclass) < 0) 1396e277ed5SThomas Graf goto errout; 14020462155SPhil Sutter 1418220ea23SPhil Sutter if (((1 << sk->sk_state) & (TCPF_LISTEN | TCPF_CLOSE)) && 1428220ea23SPhil Sutter nla_put_u8(skb, INET_DIAG_SKV6ONLY, ipv6_only_sock(sk))) 14320462155SPhil Sutter goto errout; 144a8c2190eSArnaldo Carvalho de Melo } 145a8c2190eSArnaldo Carvalho de Melo #endif 146a8c2190eSArnaldo Carvalho de Melo 147d545cacaSLorenzo Colitti if (net_admin && nla_put_u32(skb, INET_DIAG_MARK, sk->sk_mark)) 148d545cacaSLorenzo Colitti goto errout; 149d545cacaSLorenzo Colitti 150d06ca956SEric W. Biederman r->idiag_uid = from_kuid_munged(user_ns, sock_i_uid(sk)); 1513c4d05c8SPavel Emelyanov r->idiag_inode = sock_i_ino(sk); 1523c4d05c8SPavel Emelyanov 153cb2050a7SXin Long return 0; 154cb2050a7SXin Long errout: 155cb2050a7SXin Long return 1; 156cb2050a7SXin Long } 157cb2050a7SXin Long EXPORT_SYMBOL_GPL(inet_diag_msg_attrs_fill); 158cb2050a7SXin Long 159cb2050a7SXin Long int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, 160cb2050a7SXin Long struct sk_buff *skb, const struct inet_diag_req_v2 *req, 161cb2050a7SXin Long struct user_namespace *user_ns, 162cb2050a7SXin Long u32 portid, u32 seq, u16 nlmsg_flags, 163d545cacaSLorenzo Colitti const struct nlmsghdr *unlh, 164d545cacaSLorenzo Colitti bool net_admin) 165cb2050a7SXin Long { 166cb2050a7SXin Long const struct tcp_congestion_ops *ca_ops; 167cb2050a7SXin Long const struct inet_diag_handler *handler; 168cb2050a7SXin Long int ext = req->idiag_ext; 169cb2050a7SXin Long struct inet_diag_msg *r; 170cb2050a7SXin Long struct nlmsghdr *nlh; 171cb2050a7SXin Long struct nlattr *attr; 172cb2050a7SXin Long void *info = NULL; 173cb2050a7SXin Long 174cb2050a7SXin Long handler = inet_diag_table[req->sdiag_protocol]; 175cb2050a7SXin Long BUG_ON(!handler); 176cb2050a7SXin Long 177cb2050a7SXin Long nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r), 178cb2050a7SXin Long nlmsg_flags); 179cb2050a7SXin Long if (!nlh) 180cb2050a7SXin Long return -EMSGSIZE; 181cb2050a7SXin Long 182cb2050a7SXin Long r = nlmsg_data(nlh); 183cb2050a7SXin Long BUG_ON(!sk_fullsock(sk)); 184cb2050a7SXin Long 185cb2050a7SXin Long inet_diag_msg_common_fill(r, sk); 186cb2050a7SXin Long r->idiag_state = sk->sk_state; 187cb2050a7SXin Long r->idiag_timer = 0; 188cb2050a7SXin Long r->idiag_retrans = 0; 189cb2050a7SXin Long 190d545cacaSLorenzo Colitti if (inet_diag_msg_attrs_fill(sk, skb, r, ext, user_ns, net_admin)) 191cb2050a7SXin Long goto errout; 192cb2050a7SXin Long 1936e277ed5SThomas Graf if (ext & (1 << (INET_DIAG_MEMINFO - 1))) { 1946e277ed5SThomas Graf struct inet_diag_meminfo minfo = { 1956e277ed5SThomas Graf .idiag_rmem = sk_rmem_alloc_get(sk), 196*ab4e846aSEric Dumazet .idiag_wmem = READ_ONCE(sk->sk_wmem_queued), 1976e277ed5SThomas Graf .idiag_fmem = sk->sk_forward_alloc, 1986e277ed5SThomas Graf .idiag_tmem = sk_wmem_alloc_get(sk), 1996e277ed5SThomas Graf }; 2006e277ed5SThomas Graf 2016e277ed5SThomas Graf if (nla_put(skb, INET_DIAG_MEMINFO, sizeof(minfo), &minfo) < 0) 2026e277ed5SThomas Graf goto errout; 2033c4d05c8SPavel Emelyanov } 2043c4d05c8SPavel Emelyanov 205c0636faaSPavel Emelyanov if (ext & (1 << (INET_DIAG_SKMEMINFO - 1))) 206c0636faaSPavel Emelyanov if (sock_diag_put_meminfo(sk, skb, INET_DIAG_SKMEMINFO)) 2076e277ed5SThomas Graf goto errout; 208c0636faaSPavel Emelyanov 209432490f9SCyrill Gorcunov /* 210432490f9SCyrill Gorcunov * RAW sockets might have user-defined protocols assigned, 211432490f9SCyrill Gorcunov * so report the one supplied on socket creation. 212432490f9SCyrill Gorcunov */ 213432490f9SCyrill Gorcunov if (sk->sk_type == SOCK_RAW) { 214432490f9SCyrill Gorcunov if (nla_put_u8(skb, INET_DIAG_PROTOCOL, sk->sk_protocol)) 215432490f9SCyrill Gorcunov goto errout; 216432490f9SCyrill Gorcunov } 217432490f9SCyrill Gorcunov 218e31c5e0eSEric Dumazet if (!icsk) { 21962ad6fcdSShan Wei handler->idiag_get_info(sk, r, NULL); 2203c4d05c8SPavel Emelyanov goto out; 2213c4d05c8SPavel Emelyanov } 2223c4d05c8SPavel Emelyanov 2236ba8a3b1SNandita Dukkipati if (icsk->icsk_pending == ICSK_TIME_RETRANS || 22457dde7f7SYuchung Cheng icsk->icsk_pending == ICSK_TIME_REO_TIMEOUT || 2256ba8a3b1SNandita Dukkipati icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) { 226a8c2190eSArnaldo Carvalho de Melo r->idiag_timer = 1; 227a8c2190eSArnaldo Carvalho de Melo r->idiag_retrans = icsk->icsk_retransmits; 228b7de529cSXin Long r->idiag_expires = 229b7de529cSXin Long jiffies_to_msecs(icsk->icsk_timeout - jiffies); 230a8c2190eSArnaldo Carvalho de Melo } else if (icsk->icsk_pending == ICSK_TIME_PROBE0) { 231a8c2190eSArnaldo Carvalho de Melo r->idiag_timer = 4; 232a8c2190eSArnaldo Carvalho de Melo r->idiag_retrans = icsk->icsk_probes_out; 233b7de529cSXin Long r->idiag_expires = 234b7de529cSXin Long jiffies_to_msecs(icsk->icsk_timeout - jiffies); 235a8c2190eSArnaldo Carvalho de Melo } else if (timer_pending(&sk->sk_timer)) { 236a8c2190eSArnaldo Carvalho de Melo r->idiag_timer = 2; 237a8c2190eSArnaldo Carvalho de Melo r->idiag_retrans = icsk->icsk_probes_out; 238b7de529cSXin Long r->idiag_expires = 239b7de529cSXin Long jiffies_to_msecs(sk->sk_timer.expires - jiffies); 240a8c2190eSArnaldo Carvalho de Melo } else { 241a8c2190eSArnaldo Carvalho de Melo r->idiag_timer = 0; 242a8c2190eSArnaldo Carvalho de Melo r->idiag_expires = 0; 243a8c2190eSArnaldo Carvalho de Melo } 244a8c2190eSArnaldo Carvalho de Melo 2453fd22af8SCraig Gallek if ((ext & (1 << (INET_DIAG_INFO - 1))) && handler->idiag_info_size) { 2466ed46d12SNicolas Dichtel attr = nla_reserve_64bit(skb, INET_DIAG_INFO, 2476ed46d12SNicolas Dichtel handler->idiag_info_size, 2486ed46d12SNicolas Dichtel INET_DIAG_PAD); 2496e277ed5SThomas Graf if (!attr) 2506e277ed5SThomas Graf goto errout; 251a8c2190eSArnaldo Carvalho de Melo 2526e277ed5SThomas Graf info = nla_data(attr); 253a8c2190eSArnaldo Carvalho de Melo } 254a8c2190eSArnaldo Carvalho de Melo 255521f1cf1SEric Dumazet if (ext & (1 << (INET_DIAG_CONG - 1))) { 256521f1cf1SEric Dumazet int err = 0; 257521f1cf1SEric Dumazet 258521f1cf1SEric Dumazet rcu_read_lock(); 259521f1cf1SEric Dumazet ca_ops = READ_ONCE(icsk->icsk_ca_ops); 260521f1cf1SEric Dumazet if (ca_ops) 261521f1cf1SEric Dumazet err = nla_put_string(skb, INET_DIAG_CONG, ca_ops->name); 262521f1cf1SEric Dumazet rcu_read_unlock(); 263521f1cf1SEric Dumazet if (err < 0) 2646e277ed5SThomas Graf goto errout; 265521f1cf1SEric Dumazet } 2666e277ed5SThomas Graf 267a8c2190eSArnaldo Carvalho de Melo handler->idiag_get_info(sk, r, info); 268a8c2190eSArnaldo Carvalho de Melo 269b37e8840SIvan Delalande if (ext & (1 << (INET_DIAG_INFO - 1)) && handler->idiag_get_aux) 270b37e8840SIvan Delalande if (handler->idiag_get_aux(sk, net_admin, skb) < 0) 271b37e8840SIvan Delalande goto errout; 272b37e8840SIvan Delalande 273521f1cf1SEric Dumazet if (sk->sk_state < TCP_TIME_WAIT) { 27464f40ff5SEric Dumazet union tcp_cc_info info; 27564f40ff5SEric Dumazet size_t sz = 0; 27664f40ff5SEric Dumazet int attr; 277521f1cf1SEric Dumazet 278521f1cf1SEric Dumazet rcu_read_lock(); 279521f1cf1SEric Dumazet ca_ops = READ_ONCE(icsk->icsk_ca_ops); 280521f1cf1SEric Dumazet if (ca_ops && ca_ops->get_info) 28164f40ff5SEric Dumazet sz = ca_ops->get_info(sk, ext, &attr, &info); 282521f1cf1SEric Dumazet rcu_read_unlock(); 28364f40ff5SEric Dumazet if (sz && nla_put(skb, attr, sz, &info) < 0) 284521f1cf1SEric Dumazet goto errout; 285521f1cf1SEric Dumazet } 286a8c2190eSArnaldo Carvalho de Melo 2871ec17dbdSKonstantin Khlebnikov if (ext & (1 << (INET_DIAG_CLASS_ID - 1)) || 2881ec17dbdSKonstantin Khlebnikov ext & (1 << (INET_DIAG_TCLASS - 1))) { 2890888e372SLevin, Alexander (Sasha Levin) u32 classid = 0; 2900888e372SLevin, Alexander (Sasha Levin) 2910888e372SLevin, Alexander (Sasha Levin) #ifdef CONFIG_SOCK_CGROUP_DATA 2920888e372SLevin, Alexander (Sasha Levin) classid = sock_cgroup_classid(&sk->sk_cgrp_data); 2930888e372SLevin, Alexander (Sasha Levin) #endif 2941ec17dbdSKonstantin Khlebnikov /* Fallback to socket priority if class id isn't set. 2951ec17dbdSKonstantin Khlebnikov * Classful qdiscs use it as direct reference to class. 2961ec17dbdSKonstantin Khlebnikov * For cgroup2 classid is always zero. 2971ec17dbdSKonstantin Khlebnikov */ 2981ec17dbdSKonstantin Khlebnikov if (!classid) 2991ec17dbdSKonstantin Khlebnikov classid = sk->sk_priority; 3000888e372SLevin, Alexander (Sasha Levin) 3010888e372SLevin, Alexander (Sasha Levin) if (nla_put_u32(skb, INET_DIAG_CLASS_ID, classid)) 3020888e372SLevin, Alexander (Sasha Levin) goto errout; 3030888e372SLevin, Alexander (Sasha Levin) } 3040888e372SLevin, Alexander (Sasha Levin) 3053c4d05c8SPavel Emelyanov out: 306053c095aSJohannes Berg nlmsg_end(skb, nlh); 307053c095aSJohannes Berg return 0; 308a8c2190eSArnaldo Carvalho de Melo 3096e277ed5SThomas Graf errout: 3106e277ed5SThomas Graf nlmsg_cancel(skb, nlh); 31126932566SPatrick McHardy return -EMSGSIZE; 312a8c2190eSArnaldo Carvalho de Melo } 3133c4d05c8SPavel Emelyanov EXPORT_SYMBOL_GPL(inet_sk_diag_fill); 3143c4d05c8SPavel Emelyanov 3153c4d05c8SPavel Emelyanov static int inet_csk_diag_fill(struct sock *sk, 316e31c5e0eSEric Dumazet struct sk_buff *skb, 31734160ea3SEric Dumazet const struct inet_diag_req_v2 *req, 318d06ca956SEric W. Biederman struct user_namespace *user_ns, 31915e47304SEric W. Biederman u32 portid, u32 seq, u16 nlmsg_flags, 320d545cacaSLorenzo Colitti const struct nlmsghdr *unlh, 321d545cacaSLorenzo Colitti bool net_admin) 3223c4d05c8SPavel Emelyanov { 323d545cacaSLorenzo Colitti return inet_sk_diag_fill(sk, inet_csk(sk), skb, req, user_ns, 324d545cacaSLorenzo Colitti portid, seq, nlmsg_flags, unlh, net_admin); 3253c4d05c8SPavel Emelyanov } 326a8c2190eSArnaldo Carvalho de Melo 32733cf7c90SEric Dumazet static int inet_twsk_diag_fill(struct sock *sk, 328e31c5e0eSEric Dumazet struct sk_buff *skb, 32915e47304SEric W. Biederman u32 portid, u32 seq, u16 nlmsg_flags, 330c7d58aabSArnaldo Carvalho de Melo const struct nlmsghdr *unlh) 331c7d58aabSArnaldo Carvalho de Melo { 33233cf7c90SEric Dumazet struct inet_timewait_sock *tw = inet_twsk(sk); 333c7d58aabSArnaldo Carvalho de Melo struct inet_diag_msg *r; 3346e277ed5SThomas Graf struct nlmsghdr *nlh; 335789f558cSEric Dumazet long tmo; 336c7d58aabSArnaldo Carvalho de Melo 33715e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r), 3386e277ed5SThomas Graf nlmsg_flags); 3396e277ed5SThomas Graf if (!nlh) 340d106352dSDavid S. Miller return -EMSGSIZE; 341d106352dSDavid S. Miller 342d106352dSDavid S. Miller r = nlmsg_data(nlh); 343c7d58aabSArnaldo Carvalho de Melo BUG_ON(tw->tw_state != TCP_TIME_WAIT); 344c7d58aabSArnaldo Carvalho de Melo 345789f558cSEric Dumazet tmo = tw->tw_timer.expires - jiffies; 346c7d58aabSArnaldo Carvalho de Melo if (tmo < 0) 347c7d58aabSArnaldo Carvalho de Melo tmo = 0; 348c7d58aabSArnaldo Carvalho de Melo 349a4458343SEric Dumazet inet_diag_msg_common_fill(r, sk); 350c7d58aabSArnaldo Carvalho de Melo r->idiag_retrans = 0; 351b1aac815SDaniel Borkmann 352c7d58aabSArnaldo Carvalho de Melo r->idiag_state = tw->tw_substate; 353c7d58aabSArnaldo Carvalho de Melo r->idiag_timer = 3; 35496f817feSEric Dumazet r->idiag_expires = jiffies_to_msecs(tmo); 355c7d58aabSArnaldo Carvalho de Melo r->idiag_rqueue = 0; 356c7d58aabSArnaldo Carvalho de Melo r->idiag_wqueue = 0; 357c7d58aabSArnaldo Carvalho de Melo r->idiag_uid = 0; 358c7d58aabSArnaldo Carvalho de Melo r->idiag_inode = 0; 3596e277ed5SThomas Graf 360053c095aSJohannes Berg nlmsg_end(skb, nlh); 361053c095aSJohannes Berg return 0; 362c7d58aabSArnaldo Carvalho de Melo } 363c7d58aabSArnaldo Carvalho de Melo 364a58917f5SEric Dumazet static int inet_req_diag_fill(struct sock *sk, struct sk_buff *skb, 365a58917f5SEric Dumazet u32 portid, u32 seq, u16 nlmsg_flags, 366d545cacaSLorenzo Colitti const struct nlmsghdr *unlh, bool net_admin) 367a58917f5SEric Dumazet { 368d545cacaSLorenzo Colitti struct request_sock *reqsk = inet_reqsk(sk); 369a58917f5SEric Dumazet struct inet_diag_msg *r; 370a58917f5SEric Dumazet struct nlmsghdr *nlh; 371a58917f5SEric Dumazet long tmo; 372a58917f5SEric Dumazet 373a58917f5SEric Dumazet nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r), 374a58917f5SEric Dumazet nlmsg_flags); 375a58917f5SEric Dumazet if (!nlh) 376a58917f5SEric Dumazet return -EMSGSIZE; 377a58917f5SEric Dumazet 378a58917f5SEric Dumazet r = nlmsg_data(nlh); 379a58917f5SEric Dumazet inet_diag_msg_common_fill(r, sk); 380a58917f5SEric Dumazet r->idiag_state = TCP_SYN_RECV; 381a58917f5SEric Dumazet r->idiag_timer = 1; 382d545cacaSLorenzo Colitti r->idiag_retrans = reqsk->num_retrans; 383a58917f5SEric Dumazet 384a58917f5SEric Dumazet BUILD_BUG_ON(offsetof(struct inet_request_sock, ir_cookie) != 385a58917f5SEric Dumazet offsetof(struct sock, sk_cookie)); 386a58917f5SEric Dumazet 387fa76ce73SEric Dumazet tmo = inet_reqsk(sk)->rsk_timer.expires - jiffies; 388a58917f5SEric Dumazet r->idiag_expires = (tmo >= 0) ? jiffies_to_msecs(tmo) : 0; 389a58917f5SEric Dumazet r->idiag_rqueue = 0; 390a58917f5SEric Dumazet r->idiag_wqueue = 0; 391a58917f5SEric Dumazet r->idiag_uid = 0; 392a58917f5SEric Dumazet r->idiag_inode = 0; 393a58917f5SEric Dumazet 394d545cacaSLorenzo Colitti if (net_admin && nla_put_u32(skb, INET_DIAG_MARK, 395d545cacaSLorenzo Colitti inet_rsk(reqsk)->ir_mark)) 396d545cacaSLorenzo Colitti return -EMSGSIZE; 397d545cacaSLorenzo Colitti 398a58917f5SEric Dumazet nlmsg_end(skb, nlh); 399a58917f5SEric Dumazet return 0; 400a58917f5SEric Dumazet } 401a58917f5SEric Dumazet 402dff2c035SArnaldo Carvalho de Melo static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, 40334160ea3SEric Dumazet const struct inet_diag_req_v2 *r, 404d06ca956SEric W. Biederman struct user_namespace *user_ns, 40515e47304SEric W. Biederman u32 portid, u32 seq, u16 nlmsg_flags, 406d545cacaSLorenzo Colitti const struct nlmsghdr *unlh, bool net_admin) 407dff2c035SArnaldo Carvalho de Melo { 408dff2c035SArnaldo Carvalho de Melo if (sk->sk_state == TCP_TIME_WAIT) 409a58917f5SEric Dumazet return inet_twsk_diag_fill(sk, skb, portid, seq, 410a58917f5SEric Dumazet nlmsg_flags, unlh); 411a58917f5SEric Dumazet 412a58917f5SEric Dumazet if (sk->sk_state == TCP_NEW_SYN_RECV) 413a58917f5SEric Dumazet return inet_req_diag_fill(sk, skb, portid, seq, 414d545cacaSLorenzo Colitti nlmsg_flags, unlh, net_admin); 415efe4208fSEric Dumazet 416efe4208fSEric Dumazet return inet_csk_diag_fill(sk, skb, r, user_ns, portid, seq, 417d545cacaSLorenzo Colitti nlmsg_flags, unlh, net_admin); 418dff2c035SArnaldo Carvalho de Melo } 419dff2c035SArnaldo Carvalho de Melo 420b613f56eSLorenzo Colitti struct sock *inet_diag_find_one_icsk(struct net *net, 421b613f56eSLorenzo Colitti struct inet_hashinfo *hashinfo, 42234160ea3SEric Dumazet const struct inet_diag_req_v2 *req) 423a8c2190eSArnaldo Carvalho de Melo { 424e31c5e0eSEric Dumazet struct sock *sk; 425a8c2190eSArnaldo Carvalho de Melo 4262d331915SEric Dumazet rcu_read_lock(); 427e31c5e0eSEric Dumazet if (req->sdiag_family == AF_INET) 428a583636aSCraig Gallek sk = inet_lookup(net, hashinfo, NULL, 0, req->id.idiag_dst[0], 429a8c2190eSArnaldo Carvalho de Melo req->id.idiag_dport, req->id.idiag_src[0], 430a8c2190eSArnaldo Carvalho de Melo req->id.idiag_sport, req->id.idiag_if); 431dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 4327c130672SEric Dumazet else if (req->sdiag_family == AF_INET6) { 4337c130672SEric Dumazet if (ipv6_addr_v4mapped((struct in6_addr *)req->id.idiag_dst) && 4347c130672SEric Dumazet ipv6_addr_v4mapped((struct in6_addr *)req->id.idiag_src)) 435a583636aSCraig Gallek sk = inet_lookup(net, hashinfo, NULL, 0, req->id.idiag_dst[3], 4367c130672SEric Dumazet req->id.idiag_dport, req->id.idiag_src[3], 4377c130672SEric Dumazet req->id.idiag_sport, req->id.idiag_if); 4387c130672SEric Dumazet else 439a583636aSCraig Gallek sk = inet6_lookup(net, hashinfo, NULL, 0, 440a8c2190eSArnaldo Carvalho de Melo (struct in6_addr *)req->id.idiag_dst, 441a8c2190eSArnaldo Carvalho de Melo req->id.idiag_dport, 442a8c2190eSArnaldo Carvalho de Melo (struct in6_addr *)req->id.idiag_src, 443a8c2190eSArnaldo Carvalho de Melo req->id.idiag_sport, 444a8c2190eSArnaldo Carvalho de Melo req->id.idiag_if); 4457c130672SEric Dumazet } 446a8c2190eSArnaldo Carvalho de Melo #endif 4472d331915SEric Dumazet else { 4482d331915SEric Dumazet rcu_read_unlock(); 449b613f56eSLorenzo Colitti return ERR_PTR(-EINVAL); 4502d331915SEric Dumazet } 4512d331915SEric Dumazet rcu_read_unlock(); 452e31c5e0eSEric Dumazet if (!sk) 453b613f56eSLorenzo Colitti return ERR_PTR(-ENOENT); 454a8c2190eSArnaldo Carvalho de Melo 455b613f56eSLorenzo Colitti if (sock_diag_check_cookie(sk, req->id.idiag_cookie)) { 456b613f56eSLorenzo Colitti sock_gen_put(sk); 457b613f56eSLorenzo Colitti return ERR_PTR(-ENOENT); 458b613f56eSLorenzo Colitti } 459b613f56eSLorenzo Colitti 460b613f56eSLorenzo Colitti return sk; 461b613f56eSLorenzo Colitti } 462b613f56eSLorenzo Colitti EXPORT_SYMBOL_GPL(inet_diag_find_one_icsk); 463b613f56eSLorenzo Colitti 464b613f56eSLorenzo Colitti int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, 465b613f56eSLorenzo Colitti struct sk_buff *in_skb, 466b613f56eSLorenzo Colitti const struct nlmsghdr *nlh, 467b613f56eSLorenzo Colitti const struct inet_diag_req_v2 *req) 468b613f56eSLorenzo Colitti { 469b37e8840SIvan Delalande bool net_admin = netlink_net_capable(in_skb, CAP_NET_ADMIN); 470b613f56eSLorenzo Colitti struct net *net = sock_net(in_skb->sk); 471b613f56eSLorenzo Colitti struct sk_buff *rep; 472b613f56eSLorenzo Colitti struct sock *sk; 473b613f56eSLorenzo Colitti int err; 474b613f56eSLorenzo Colitti 475b613f56eSLorenzo Colitti sk = inet_diag_find_one_icsk(net, hashinfo, req); 476b613f56eSLorenzo Colitti if (IS_ERR(sk)) 477b613f56eSLorenzo Colitti return PTR_ERR(sk); 478a8c2190eSArnaldo Carvalho de Melo 479b37e8840SIvan Delalande rep = nlmsg_new(inet_sk_attr_size(sk, req, net_admin), GFP_KERNEL); 4806e277ed5SThomas Graf if (!rep) { 4816e277ed5SThomas Graf err = -ENOMEM; 482a8c2190eSArnaldo Carvalho de Melo goto out; 4836e277ed5SThomas Graf } 484a8c2190eSArnaldo Carvalho de Melo 485a029fe26SPavel Emelyanov err = sk_diag_fill(sk, rep, req, 486e32123e5SPatrick McHardy sk_user_ns(NETLINK_CB(in_skb).sk), 48715e47304SEric W. Biederman NETLINK_CB(in_skb).portid, 488b37e8840SIvan Delalande nlh->nlmsg_seq, 0, nlh, net_admin); 48926932566SPatrick McHardy if (err < 0) { 49026932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 4916e277ed5SThomas Graf nlmsg_free(rep); 49226932566SPatrick McHardy goto out; 49326932566SPatrick McHardy } 49415e47304SEric W. Biederman err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid, 495a8c2190eSArnaldo Carvalho de Melo MSG_DONTWAIT); 496a8c2190eSArnaldo Carvalho de Melo if (err > 0) 497a8c2190eSArnaldo Carvalho de Melo err = 0; 498a8c2190eSArnaldo Carvalho de Melo 499a8c2190eSArnaldo Carvalho de Melo out: 500c1d607ccSEric Dumazet if (sk) 501c1d607ccSEric Dumazet sock_gen_put(sk); 502c1d607ccSEric Dumazet 503476f7dbfSPavel Emelyanov return err; 504476f7dbfSPavel Emelyanov } 5051942c518SPavel Emelyanov EXPORT_SYMBOL_GPL(inet_diag_dump_one_icsk); 506476f7dbfSPavel Emelyanov 5076eb5d2e0SLorenzo Colitti static int inet_diag_cmd_exact(int cmd, struct sk_buff *in_skb, 508476f7dbfSPavel Emelyanov const struct nlmsghdr *nlh, 50934160ea3SEric Dumazet const struct inet_diag_req_v2 *req) 510476f7dbfSPavel Emelyanov { 511476f7dbfSPavel Emelyanov const struct inet_diag_handler *handler; 512476f7dbfSPavel Emelyanov int err; 513476f7dbfSPavel Emelyanov 514476f7dbfSPavel Emelyanov handler = inet_diag_lock_handler(req->sdiag_protocol); 515476f7dbfSPavel Emelyanov if (IS_ERR(handler)) 516476f7dbfSPavel Emelyanov err = PTR_ERR(handler); 5176eb5d2e0SLorenzo Colitti else if (cmd == SOCK_DIAG_BY_FAMILY) 5181942c518SPavel Emelyanov err = handler->dump_one(in_skb, nlh, req); 5196eb5d2e0SLorenzo Colitti else if (cmd == SOCK_DESTROY && handler->destroy) 5206eb5d2e0SLorenzo Colitti err = handler->destroy(in_skb, req); 5216eb5d2e0SLorenzo Colitti else 5226eb5d2e0SLorenzo Colitti err = -EOPNOTSUPP; 523d523a328SHerbert Xu inet_diag_unlock_handler(handler); 524476f7dbfSPavel Emelyanov 525a8c2190eSArnaldo Carvalho de Melo return err; 526a8c2190eSArnaldo Carvalho de Melo } 527a8c2190eSArnaldo Carvalho de Melo 5289f855299SAl Viro static int bitstring_match(const __be32 *a1, const __be32 *a2, int bits) 529a8c2190eSArnaldo Carvalho de Melo { 530a8c2190eSArnaldo Carvalho de Melo int words = bits >> 5; 531a8c2190eSArnaldo Carvalho de Melo 532a8c2190eSArnaldo Carvalho de Melo bits &= 0x1f; 533a8c2190eSArnaldo Carvalho de Melo 534a8c2190eSArnaldo Carvalho de Melo if (words) { 535a8c2190eSArnaldo Carvalho de Melo if (memcmp(a1, a2, words << 2)) 536a8c2190eSArnaldo Carvalho de Melo return 0; 537a8c2190eSArnaldo Carvalho de Melo } 538a8c2190eSArnaldo Carvalho de Melo if (bits) { 5399f855299SAl Viro __be32 w1, w2; 5409f855299SAl Viro __be32 mask; 541a8c2190eSArnaldo Carvalho de Melo 542a8c2190eSArnaldo Carvalho de Melo w1 = a1[words]; 543a8c2190eSArnaldo Carvalho de Melo w2 = a2[words]; 544a8c2190eSArnaldo Carvalho de Melo 545a8c2190eSArnaldo Carvalho de Melo mask = htonl((0xffffffff) << (32 - bits)); 546a8c2190eSArnaldo Carvalho de Melo 547a8c2190eSArnaldo Carvalho de Melo if ((w1 ^ w2) & mask) 548a8c2190eSArnaldo Carvalho de Melo return 0; 549a8c2190eSArnaldo Carvalho de Melo } 550a8c2190eSArnaldo Carvalho de Melo 551a8c2190eSArnaldo Carvalho de Melo return 1; 552a8c2190eSArnaldo Carvalho de Melo } 553a8c2190eSArnaldo Carvalho de Melo 55487c22ea5SPavel Emelyanov static int inet_diag_bc_run(const struct nlattr *_bc, 555a8c2190eSArnaldo Carvalho de Melo const struct inet_diag_entry *entry) 556a8c2190eSArnaldo Carvalho de Melo { 55787c22ea5SPavel Emelyanov const void *bc = nla_data(_bc); 55887c22ea5SPavel Emelyanov int len = nla_len(_bc); 55987c22ea5SPavel Emelyanov 560a8c2190eSArnaldo Carvalho de Melo while (len > 0) { 561a8c2190eSArnaldo Carvalho de Melo int yes = 1; 562a8c2190eSArnaldo Carvalho de Melo const struct inet_diag_bc_op *op = bc; 563a8c2190eSArnaldo Carvalho de Melo 564a8c2190eSArnaldo Carvalho de Melo switch (op->code) { 565a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_NOP: 566a8c2190eSArnaldo Carvalho de Melo break; 567a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_JMP: 568a8c2190eSArnaldo Carvalho de Melo yes = 0; 569a8c2190eSArnaldo Carvalho de Melo break; 570bbb6189dSKristian Evensen case INET_DIAG_BC_S_EQ: 571bbb6189dSKristian Evensen yes = entry->sport == op[1].no; 572bbb6189dSKristian Evensen break; 573a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_S_GE: 574a8c2190eSArnaldo Carvalho de Melo yes = entry->sport >= op[1].no; 575a8c2190eSArnaldo Carvalho de Melo break; 576a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_S_LE: 577b4ced2b7SRoel Kluin yes = entry->sport <= op[1].no; 578a8c2190eSArnaldo Carvalho de Melo break; 579bbb6189dSKristian Evensen case INET_DIAG_BC_D_EQ: 580bbb6189dSKristian Evensen yes = entry->dport == op[1].no; 581bbb6189dSKristian Evensen break; 582a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_D_GE: 583a8c2190eSArnaldo Carvalho de Melo yes = entry->dport >= op[1].no; 584a8c2190eSArnaldo Carvalho de Melo break; 585a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_D_LE: 586a8c2190eSArnaldo Carvalho de Melo yes = entry->dport <= op[1].no; 587a8c2190eSArnaldo Carvalho de Melo break; 588a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_AUTO: 589a8c2190eSArnaldo Carvalho de Melo yes = !(entry->userlocks & SOCK_BINDPORT_LOCK); 590a8c2190eSArnaldo Carvalho de Melo break; 591a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_S_COND: 592a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_D_COND: { 593e31c5e0eSEric Dumazet const struct inet_diag_hostcond *cond; 594e31c5e0eSEric Dumazet const __be32 *addr; 595a8c2190eSArnaldo Carvalho de Melo 596e31c5e0eSEric Dumazet cond = (const struct inet_diag_hostcond *)(op + 1); 597a8c2190eSArnaldo Carvalho de Melo if (cond->port != -1 && 598a8c2190eSArnaldo Carvalho de Melo cond->port != (op->code == INET_DIAG_BC_S_COND ? 599a8c2190eSArnaldo Carvalho de Melo entry->sport : entry->dport)) { 600a8c2190eSArnaldo Carvalho de Melo yes = 0; 601a8c2190eSArnaldo Carvalho de Melo break; 602a8c2190eSArnaldo Carvalho de Melo } 603a8c2190eSArnaldo Carvalho de Melo 604a8c2190eSArnaldo Carvalho de Melo if (op->code == INET_DIAG_BC_S_COND) 605a8c2190eSArnaldo Carvalho de Melo addr = entry->saddr; 606a8c2190eSArnaldo Carvalho de Melo else 607a8c2190eSArnaldo Carvalho de Melo addr = entry->daddr; 608a8c2190eSArnaldo Carvalho de Melo 609f67caec9SNeal Cardwell if (cond->family != AF_UNSPEC && 610f67caec9SNeal Cardwell cond->family != entry->family) { 611a8c2190eSArnaldo Carvalho de Melo if (entry->family == AF_INET6 && 612a8c2190eSArnaldo Carvalho de Melo cond->family == AF_INET) { 613a8c2190eSArnaldo Carvalho de Melo if (addr[0] == 0 && addr[1] == 0 && 614a8c2190eSArnaldo Carvalho de Melo addr[2] == htonl(0xffff) && 615f67caec9SNeal Cardwell bitstring_match(addr + 3, 616f67caec9SNeal Cardwell cond->addr, 617a8c2190eSArnaldo Carvalho de Melo cond->prefix_len)) 618a8c2190eSArnaldo Carvalho de Melo break; 619a8c2190eSArnaldo Carvalho de Melo } 620a8c2190eSArnaldo Carvalho de Melo yes = 0; 621a8c2190eSArnaldo Carvalho de Melo break; 622a8c2190eSArnaldo Carvalho de Melo } 623f67caec9SNeal Cardwell 624f67caec9SNeal Cardwell if (cond->prefix_len == 0) 625f67caec9SNeal Cardwell break; 626f67caec9SNeal Cardwell if (bitstring_match(addr, cond->addr, 627f67caec9SNeal Cardwell cond->prefix_len)) 628f67caec9SNeal Cardwell break; 629f67caec9SNeal Cardwell yes = 0; 630f67caec9SNeal Cardwell break; 631f67caec9SNeal Cardwell } 632637c841dSDavid Ahern case INET_DIAG_BC_DEV_COND: { 633637c841dSDavid Ahern u32 ifindex; 634637c841dSDavid Ahern 635637c841dSDavid Ahern ifindex = *((const u32 *)(op + 1)); 636637c841dSDavid Ahern if (ifindex != entry->ifindex) 637637c841dSDavid Ahern yes = 0; 638637c841dSDavid Ahern break; 639637c841dSDavid Ahern } 640a52e95abSLorenzo Colitti case INET_DIAG_BC_MARK_COND: { 641a52e95abSLorenzo Colitti struct inet_diag_markcond *cond; 642a52e95abSLorenzo Colitti 643a52e95abSLorenzo Colitti cond = (struct inet_diag_markcond *)(op + 1); 644a52e95abSLorenzo Colitti if ((entry->mark & cond->mask) != cond->mark) 645a52e95abSLorenzo Colitti yes = 0; 646a52e95abSLorenzo Colitti break; 647a52e95abSLorenzo Colitti } 648a8c2190eSArnaldo Carvalho de Melo } 649a8c2190eSArnaldo Carvalho de Melo 650a8c2190eSArnaldo Carvalho de Melo if (yes) { 651a8c2190eSArnaldo Carvalho de Melo len -= op->yes; 652a8c2190eSArnaldo Carvalho de Melo bc += op->yes; 653a8c2190eSArnaldo Carvalho de Melo } else { 654a8c2190eSArnaldo Carvalho de Melo len -= op->no; 655a8c2190eSArnaldo Carvalho de Melo bc += op->no; 656a8c2190eSArnaldo Carvalho de Melo } 657a8c2190eSArnaldo Carvalho de Melo } 658a02cec21SEric Dumazet return len == 0; 659a8c2190eSArnaldo Carvalho de Melo } 660a8c2190eSArnaldo Carvalho de Melo 661a4458343SEric Dumazet /* This helper is available for all sockets (ESTABLISH, TIMEWAIT, SYN_RECV) 662a4458343SEric Dumazet */ 663a4458343SEric Dumazet static void entry_fill_addrs(struct inet_diag_entry *entry, 664a4458343SEric Dumazet const struct sock *sk) 665a4458343SEric Dumazet { 666a4458343SEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 667a4458343SEric Dumazet if (sk->sk_family == AF_INET6) { 668a4458343SEric Dumazet entry->saddr = sk->sk_v6_rcv_saddr.s6_addr32; 669a4458343SEric Dumazet entry->daddr = sk->sk_v6_daddr.s6_addr32; 670a4458343SEric Dumazet } else 671a4458343SEric Dumazet #endif 672a4458343SEric Dumazet { 673a4458343SEric Dumazet entry->saddr = &sk->sk_rcv_saddr; 674a4458343SEric Dumazet entry->daddr = &sk->sk_daddr; 675a4458343SEric Dumazet } 676a4458343SEric Dumazet } 677a4458343SEric Dumazet 6788d07d151SPavel Emelyanov int inet_diag_bc_sk(const struct nlattr *bc, struct sock *sk) 6798d07d151SPavel Emelyanov { 6808d07d151SPavel Emelyanov struct inet_sock *inet = inet_sk(sk); 681e31c5e0eSEric Dumazet struct inet_diag_entry entry; 6828d07d151SPavel Emelyanov 683e31c5e0eSEric Dumazet if (!bc) 6848d07d151SPavel Emelyanov return 1; 6858d07d151SPavel Emelyanov 6868d07d151SPavel Emelyanov entry.family = sk->sk_family; 687a4458343SEric Dumazet entry_fill_addrs(&entry, sk); 6888d07d151SPavel Emelyanov entry.sport = inet->inet_num; 6898d07d151SPavel Emelyanov entry.dport = ntohs(inet->inet_dport); 690637c841dSDavid Ahern entry.ifindex = sk->sk_bound_dev_if; 691a58917f5SEric Dumazet entry.userlocks = sk_fullsock(sk) ? sk->sk_userlocks : 0; 692a52e95abSLorenzo Colitti if (sk_fullsock(sk)) 693a52e95abSLorenzo Colitti entry.mark = sk->sk_mark; 694a52e95abSLorenzo Colitti else if (sk->sk_state == TCP_NEW_SYN_RECV) 695a52e95abSLorenzo Colitti entry.mark = inet_rsk(inet_reqsk(sk))->ir_mark; 696a52e95abSLorenzo Colitti else 697a52e95abSLorenzo Colitti entry.mark = 0; 6988d07d151SPavel Emelyanov 6998d07d151SPavel Emelyanov return inet_diag_bc_run(bc, &entry); 7008d07d151SPavel Emelyanov } 7018d07d151SPavel Emelyanov EXPORT_SYMBOL_GPL(inet_diag_bc_sk); 7028d07d151SPavel Emelyanov 703a8c2190eSArnaldo Carvalho de Melo static int valid_cc(const void *bc, int len, int cc) 704a8c2190eSArnaldo Carvalho de Melo { 705a8c2190eSArnaldo Carvalho de Melo while (len >= 0) { 706a8c2190eSArnaldo Carvalho de Melo const struct inet_diag_bc_op *op = bc; 707a8c2190eSArnaldo Carvalho de Melo 708a8c2190eSArnaldo Carvalho de Melo if (cc > len) 709a8c2190eSArnaldo Carvalho de Melo return 0; 710a8c2190eSArnaldo Carvalho de Melo if (cc == len) 711a8c2190eSArnaldo Carvalho de Melo return 1; 712eeb14972SEric Dumazet if (op->yes < 4 || op->yes & 3) 713a8c2190eSArnaldo Carvalho de Melo return 0; 714a8c2190eSArnaldo Carvalho de Melo len -= op->yes; 715a8c2190eSArnaldo Carvalho de Melo bc += op->yes; 716a8c2190eSArnaldo Carvalho de Melo } 717a8c2190eSArnaldo Carvalho de Melo return 0; 718a8c2190eSArnaldo Carvalho de Melo } 719a8c2190eSArnaldo Carvalho de Melo 720637c841dSDavid Ahern /* data is u32 ifindex */ 721637c841dSDavid Ahern static bool valid_devcond(const struct inet_diag_bc_op *op, int len, 722637c841dSDavid Ahern int *min_len) 723637c841dSDavid Ahern { 724637c841dSDavid Ahern /* Check ifindex space. */ 725637c841dSDavid Ahern *min_len += sizeof(u32); 726637c841dSDavid Ahern if (len < *min_len) 727637c841dSDavid Ahern return false; 728637c841dSDavid Ahern 729637c841dSDavid Ahern return true; 730637c841dSDavid Ahern } 731405c0059SNeal Cardwell /* Validate an inet_diag_hostcond. */ 732405c0059SNeal Cardwell static bool valid_hostcond(const struct inet_diag_bc_op *op, int len, 733405c0059SNeal Cardwell int *min_len) 734405c0059SNeal Cardwell { 735405c0059SNeal Cardwell struct inet_diag_hostcond *cond; 736e31c5e0eSEric Dumazet int addr_len; 737405c0059SNeal Cardwell 738405c0059SNeal Cardwell /* Check hostcond space. */ 739405c0059SNeal Cardwell *min_len += sizeof(struct inet_diag_hostcond); 740405c0059SNeal Cardwell if (len < *min_len) 741405c0059SNeal Cardwell return false; 742405c0059SNeal Cardwell cond = (struct inet_diag_hostcond *)(op + 1); 743405c0059SNeal Cardwell 744405c0059SNeal Cardwell /* Check address family and address length. */ 745405c0059SNeal Cardwell switch (cond->family) { 746405c0059SNeal Cardwell case AF_UNSPEC: 747405c0059SNeal Cardwell addr_len = 0; 748405c0059SNeal Cardwell break; 749405c0059SNeal Cardwell case AF_INET: 750405c0059SNeal Cardwell addr_len = sizeof(struct in_addr); 751405c0059SNeal Cardwell break; 752405c0059SNeal Cardwell case AF_INET6: 753405c0059SNeal Cardwell addr_len = sizeof(struct in6_addr); 754405c0059SNeal Cardwell break; 755405c0059SNeal Cardwell default: 756405c0059SNeal Cardwell return false; 757405c0059SNeal Cardwell } 758405c0059SNeal Cardwell *min_len += addr_len; 759405c0059SNeal Cardwell if (len < *min_len) 760405c0059SNeal Cardwell return false; 761405c0059SNeal Cardwell 762405c0059SNeal Cardwell /* Check prefix length (in bits) vs address length (in bytes). */ 763405c0059SNeal Cardwell if (cond->prefix_len > 8 * addr_len) 764405c0059SNeal Cardwell return false; 765405c0059SNeal Cardwell 766405c0059SNeal Cardwell return true; 767405c0059SNeal Cardwell } 768405c0059SNeal Cardwell 7695e1f5420SNeal Cardwell /* Validate a port comparison operator. */ 770e31c5e0eSEric Dumazet static bool valid_port_comparison(const struct inet_diag_bc_op *op, 7715e1f5420SNeal Cardwell int len, int *min_len) 7725e1f5420SNeal Cardwell { 7735e1f5420SNeal Cardwell /* Port comparisons put the port in a follow-on inet_diag_bc_op. */ 7745e1f5420SNeal Cardwell *min_len += sizeof(struct inet_diag_bc_op); 7755e1f5420SNeal Cardwell if (len < *min_len) 7765e1f5420SNeal Cardwell return false; 7775e1f5420SNeal Cardwell return true; 7785e1f5420SNeal Cardwell } 7795e1f5420SNeal Cardwell 780a52e95abSLorenzo Colitti static bool valid_markcond(const struct inet_diag_bc_op *op, int len, 781a52e95abSLorenzo Colitti int *min_len) 782a8c2190eSArnaldo Carvalho de Melo { 783a52e95abSLorenzo Colitti *min_len += sizeof(struct inet_diag_markcond); 784a52e95abSLorenzo Colitti return len >= *min_len; 785a52e95abSLorenzo Colitti } 786a52e95abSLorenzo Colitti 787a52e95abSLorenzo Colitti static int inet_diag_bc_audit(const struct nlattr *attr, 788a52e95abSLorenzo Colitti const struct sk_buff *skb) 789a52e95abSLorenzo Colitti { 790a52e95abSLorenzo Colitti bool net_admin = netlink_net_capable(skb, CAP_NET_ADMIN); 791627cc4adSLorenzo Colitti const void *bytecode, *bc; 792627cc4adSLorenzo Colitti int bytecode_len, len; 793627cc4adSLorenzo Colitti 794627cc4adSLorenzo Colitti if (!attr || nla_len(attr) < sizeof(struct inet_diag_bc_op)) 795627cc4adSLorenzo Colitti return -EINVAL; 796627cc4adSLorenzo Colitti 797627cc4adSLorenzo Colitti bytecode = bc = nla_data(attr); 798627cc4adSLorenzo Colitti len = bytecode_len = nla_len(attr); 799a8c2190eSArnaldo Carvalho de Melo 800a8c2190eSArnaldo Carvalho de Melo while (len > 0) { 801405c0059SNeal Cardwell int min_len = sizeof(struct inet_diag_bc_op); 802e31c5e0eSEric Dumazet const struct inet_diag_bc_op *op = bc; 803a8c2190eSArnaldo Carvalho de Melo 804a8c2190eSArnaldo Carvalho de Melo switch (op->code) { 805a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_S_COND: 806a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_D_COND: 807405c0059SNeal Cardwell if (!valid_hostcond(bc, len, &min_len)) 808405c0059SNeal Cardwell return -EINVAL; 8095e1f5420SNeal Cardwell break; 810637c841dSDavid Ahern case INET_DIAG_BC_DEV_COND: 811637c841dSDavid Ahern if (!valid_devcond(bc, len, &min_len)) 812637c841dSDavid Ahern return -EINVAL; 813637c841dSDavid Ahern break; 814bbb6189dSKristian Evensen case INET_DIAG_BC_S_EQ: 815a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_S_GE: 816a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_S_LE: 817bbb6189dSKristian Evensen case INET_DIAG_BC_D_EQ: 818a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_D_GE: 819a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_D_LE: 8205e1f5420SNeal Cardwell if (!valid_port_comparison(bc, len, &min_len)) 821a8c2190eSArnaldo Carvalho de Melo return -EINVAL; 822a8c2190eSArnaldo Carvalho de Melo break; 823a52e95abSLorenzo Colitti case INET_DIAG_BC_MARK_COND: 824a52e95abSLorenzo Colitti if (!net_admin) 825a52e95abSLorenzo Colitti return -EPERM; 826a52e95abSLorenzo Colitti if (!valid_markcond(bc, len, &min_len)) 827a52e95abSLorenzo Colitti return -EINVAL; 828a52e95abSLorenzo Colitti break; 8295e1f5420SNeal Cardwell case INET_DIAG_BC_AUTO: 8305e1f5420SNeal Cardwell case INET_DIAG_BC_JMP: 831a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_NOP: 832a8c2190eSArnaldo Carvalho de Melo break; 833a8c2190eSArnaldo Carvalho de Melo default: 834a8c2190eSArnaldo Carvalho de Melo return -EINVAL; 835a8c2190eSArnaldo Carvalho de Melo } 8365e1f5420SNeal Cardwell 8375e1f5420SNeal Cardwell if (op->code != INET_DIAG_BC_NOP) { 8385e1f5420SNeal Cardwell if (op->no < min_len || op->no > len + 4 || op->no & 3) 8395e1f5420SNeal Cardwell return -EINVAL; 8405e1f5420SNeal Cardwell if (op->no < len && 8415e1f5420SNeal Cardwell !valid_cc(bytecode, bytecode_len, len - op->no)) 8425e1f5420SNeal Cardwell return -EINVAL; 8435e1f5420SNeal Cardwell } 8445e1f5420SNeal Cardwell 845405c0059SNeal Cardwell if (op->yes < min_len || op->yes > len + 4 || op->yes & 3) 846eeb14972SEric Dumazet return -EINVAL; 847a8c2190eSArnaldo Carvalho de Melo bc += op->yes; 848a8c2190eSArnaldo Carvalho de Melo len -= op->yes; 849a8c2190eSArnaldo Carvalho de Melo } 850a8c2190eSArnaldo Carvalho de Melo return len == 0 ? 0 : -EINVAL; 851a8c2190eSArnaldo Carvalho de Melo } 852a8c2190eSArnaldo Carvalho de Melo 853dff2c035SArnaldo Carvalho de Melo static int inet_csk_diag_dump(struct sock *sk, 854dff2c035SArnaldo Carvalho de Melo struct sk_buff *skb, 85537f352b5SPavel Emelyanov struct netlink_callback *cb, 85634160ea3SEric Dumazet const struct inet_diag_req_v2 *r, 857d545cacaSLorenzo Colitti const struct nlattr *bc, 858d545cacaSLorenzo Colitti bool net_admin) 859a8c2190eSArnaldo Carvalho de Melo { 8608d07d151SPavel Emelyanov if (!inet_diag_bc_sk(bc, sk)) 861a8c2190eSArnaldo Carvalho de Melo return 0; 862a8c2190eSArnaldo Carvalho de Melo 863a029fe26SPavel Emelyanov return inet_csk_diag_fill(sk, skb, r, 864e32123e5SPatrick McHardy sk_user_ns(NETLINK_CB(cb->skb).sk), 86515e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 866d545cacaSLorenzo Colitti cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh, 867d545cacaSLorenzo Colitti net_admin); 868a8c2190eSArnaldo Carvalho de Melo } 869a8c2190eSArnaldo Carvalho de Melo 87049612729SEric Dumazet static void twsk_build_assert(void) 87149612729SEric Dumazet { 87249612729SEric Dumazet BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_family) != 87349612729SEric Dumazet offsetof(struct sock, sk_family)); 87449612729SEric Dumazet 87549612729SEric Dumazet BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_num) != 87649612729SEric Dumazet offsetof(struct inet_sock, inet_num)); 87749612729SEric Dumazet 87849612729SEric Dumazet BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_dport) != 87949612729SEric Dumazet offsetof(struct inet_sock, inet_dport)); 88049612729SEric Dumazet 88149612729SEric Dumazet BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_rcv_saddr) != 88249612729SEric Dumazet offsetof(struct inet_sock, inet_rcv_saddr)); 88349612729SEric Dumazet 88449612729SEric Dumazet BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_daddr) != 88549612729SEric Dumazet offsetof(struct inet_sock, inet_daddr)); 88649612729SEric Dumazet 88749612729SEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 88849612729SEric Dumazet BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_v6_rcv_saddr) != 88949612729SEric Dumazet offsetof(struct sock, sk_v6_rcv_saddr)); 89049612729SEric Dumazet 89149612729SEric Dumazet BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_v6_daddr) != 89249612729SEric Dumazet offsetof(struct sock, sk_v6_daddr)); 89349612729SEric Dumazet #endif 89449612729SEric Dumazet } 89549612729SEric Dumazet 8961942c518SPavel Emelyanov void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, 897e31c5e0eSEric Dumazet struct netlink_callback *cb, 89834160ea3SEric Dumazet const struct inet_diag_req_v2 *r, struct nlattr *bc) 899a8c2190eSArnaldo Carvalho de Melo { 900d545cacaSLorenzo Colitti bool net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN); 90167db3e4bSEric Dumazet struct net *net = sock_net(skb->sk); 90267db3e4bSEric Dumazet u32 idiag_states = r->idiag_states; 90367db3e4bSEric Dumazet int i, num, s_i, s_num; 90467db3e4bSEric Dumazet struct sock *sk; 905a8c2190eSArnaldo Carvalho de Melo 906079096f1SEric Dumazet if (idiag_states & TCPF_SYN_RECV) 907079096f1SEric Dumazet idiag_states |= TCPF_NEW_SYN_RECV; 908a8c2190eSArnaldo Carvalho de Melo s_i = cb->args[1]; 909a8c2190eSArnaldo Carvalho de Melo s_num = num = cb->args[2]; 910a8c2190eSArnaldo Carvalho de Melo 911a8c2190eSArnaldo Carvalho de Melo if (cb->args[0] == 0) { 9129652dc2eSEric Dumazet if (!(idiag_states & TCPF_LISTEN) || r->id.idiag_dport) 913a8c2190eSArnaldo Carvalho de Melo goto skip_listen_ht; 914a8c2190eSArnaldo Carvalho de Melo 915a8c2190eSArnaldo Carvalho de Melo for (i = s_i; i < INET_LHTABLE_SIZE; i++) { 9165caea4eaSEric Dumazet struct inet_listen_hashbucket *ilb; 917a8c2190eSArnaldo Carvalho de Melo 918a8c2190eSArnaldo Carvalho de Melo num = 0; 9195caea4eaSEric Dumazet ilb = &hashinfo->listening_hash[i]; 9209652dc2eSEric Dumazet spin_lock(&ilb->lock); 9213b24d854SEric Dumazet sk_for_each(sk, &ilb->head) { 922a8c2190eSArnaldo Carvalho de Melo struct inet_sock *inet = inet_sk(sk); 923a8c2190eSArnaldo Carvalho de Melo 92451d7cccfSAndrey Vagin if (!net_eq(sock_net(sk), net)) 92551d7cccfSAndrey Vagin continue; 92651d7cccfSAndrey Vagin 927a8c2190eSArnaldo Carvalho de Melo if (num < s_num) { 928a8c2190eSArnaldo Carvalho de Melo num++; 929a8c2190eSArnaldo Carvalho de Melo continue; 930a8c2190eSArnaldo Carvalho de Melo } 931a8c2190eSArnaldo Carvalho de Melo 932d23deaa0SPavel Emelyanov if (r->sdiag_family != AF_UNSPEC && 933d23deaa0SPavel Emelyanov sk->sk_family != r->sdiag_family) 934d23deaa0SPavel Emelyanov goto next_listen; 935d23deaa0SPavel Emelyanov 936c720c7e8SEric Dumazet if (r->id.idiag_sport != inet->inet_sport && 937a8c2190eSArnaldo Carvalho de Melo r->id.idiag_sport) 938a8c2190eSArnaldo Carvalho de Melo goto next_listen; 939a8c2190eSArnaldo Carvalho de Melo 940d545cacaSLorenzo Colitti if (inet_csk_diag_dump(sk, skb, cb, r, 941d545cacaSLorenzo Colitti bc, net_admin) < 0) { 9429652dc2eSEric Dumazet spin_unlock(&ilb->lock); 943a8c2190eSArnaldo Carvalho de Melo goto done; 944a8c2190eSArnaldo Carvalho de Melo } 945a8c2190eSArnaldo Carvalho de Melo 946a8c2190eSArnaldo Carvalho de Melo next_listen: 947a8c2190eSArnaldo Carvalho de Melo ++num; 948a8c2190eSArnaldo Carvalho de Melo } 9499652dc2eSEric Dumazet spin_unlock(&ilb->lock); 950a8c2190eSArnaldo Carvalho de Melo 951a8c2190eSArnaldo Carvalho de Melo s_num = 0; 952a8c2190eSArnaldo Carvalho de Melo } 953a8c2190eSArnaldo Carvalho de Melo skip_listen_ht: 954a8c2190eSArnaldo Carvalho de Melo cb->args[0] = 1; 955a8c2190eSArnaldo Carvalho de Melo s_i = num = s_num = 0; 956a8c2190eSArnaldo Carvalho de Melo } 957a8c2190eSArnaldo Carvalho de Melo 958079096f1SEric Dumazet if (!(idiag_states & ~TCPF_LISTEN)) 959efb3cb42SPavel Emelyanov goto out; 960a8c2190eSArnaldo Carvalho de Melo 96167db3e4bSEric Dumazet #define SKARR_SZ 16 962f373b53bSEric Dumazet for (i = s_i; i <= hashinfo->ehash_mask; i++) { 963a8c2190eSArnaldo Carvalho de Melo struct inet_ehash_bucket *head = &hashinfo->ehash[i]; 9647e3aab4aSDavid S. Miller spinlock_t *lock = inet_ehash_lockp(hashinfo, i); 9653ab5aee7SEric Dumazet struct hlist_nulls_node *node; 96667db3e4bSEric Dumazet struct sock *sk_arr[SKARR_SZ]; 96767db3e4bSEric Dumazet int num_arr[SKARR_SZ]; 96867db3e4bSEric Dumazet int idx, accum, res; 9696be547a6SAndi Kleen 97005dbc7b5SEric Dumazet if (hlist_nulls_empty(&head->chain)) 9716be547a6SAndi Kleen continue; 9726be547a6SAndi Kleen 973a8c2190eSArnaldo Carvalho de Melo if (i > s_i) 974a8c2190eSArnaldo Carvalho de Melo s_num = 0; 975a8c2190eSArnaldo Carvalho de Melo 97667db3e4bSEric Dumazet next_chunk: 97767db3e4bSEric Dumazet num = 0; 97867db3e4bSEric Dumazet accum = 0; 9797e3aab4aSDavid S. Miller spin_lock_bh(lock); 9803ab5aee7SEric Dumazet sk_nulls_for_each(sk, node, &head->chain) { 98167db3e4bSEric Dumazet int state; 982a8c2190eSArnaldo Carvalho de Melo 98351d7cccfSAndrey Vagin if (!net_eq(sock_net(sk), net)) 98451d7cccfSAndrey Vagin continue; 985a8c2190eSArnaldo Carvalho de Melo if (num < s_num) 986a8c2190eSArnaldo Carvalho de Melo goto next_normal; 98770315d22SNeal Cardwell state = (sk->sk_state == TCP_TIME_WAIT) ? 98870315d22SNeal Cardwell inet_twsk(sk)->tw_substate : sk->sk_state; 989079096f1SEric Dumazet if (!(idiag_states & (1 << state))) 990a8c2190eSArnaldo Carvalho de Melo goto next_normal; 991d23deaa0SPavel Emelyanov if (r->sdiag_family != AF_UNSPEC && 992d23deaa0SPavel Emelyanov sk->sk_family != r->sdiag_family) 993d23deaa0SPavel Emelyanov goto next_normal; 99405dbc7b5SEric Dumazet if (r->id.idiag_sport != htons(sk->sk_num) && 995a8c2190eSArnaldo Carvalho de Melo r->id.idiag_sport) 996a8c2190eSArnaldo Carvalho de Melo goto next_normal; 99705dbc7b5SEric Dumazet if (r->id.idiag_dport != sk->sk_dport && 9984e852c02SArnaldo Carvalho de Melo r->id.idiag_dport) 999a8c2190eSArnaldo Carvalho de Melo goto next_normal; 1000a58917f5SEric Dumazet twsk_build_assert(); 1001a58917f5SEric Dumazet 1002a58917f5SEric Dumazet if (!inet_diag_bc_sk(bc, sk)) 1003a58917f5SEric Dumazet goto next_normal; 1004a58917f5SEric Dumazet 1005f0c928d8SEric Dumazet if (!refcount_inc_not_zero(&sk->sk_refcnt)) 1006f0c928d8SEric Dumazet goto next_normal; 1007f0c928d8SEric Dumazet 100867db3e4bSEric Dumazet num_arr[accum] = num; 100967db3e4bSEric Dumazet sk_arr[accum] = sk; 101067db3e4bSEric Dumazet if (++accum == SKARR_SZ) 101167db3e4bSEric Dumazet break; 101267db3e4bSEric Dumazet next_normal: 101367db3e4bSEric Dumazet ++num; 101467db3e4bSEric Dumazet } 101567db3e4bSEric Dumazet spin_unlock_bh(lock); 101667db3e4bSEric Dumazet res = 0; 101767db3e4bSEric Dumazet for (idx = 0; idx < accum; idx++) { 101867db3e4bSEric Dumazet if (res >= 0) { 101967db3e4bSEric Dumazet res = sk_diag_fill(sk_arr[idx], skb, r, 1020a58917f5SEric Dumazet sk_user_ns(NETLINK_CB(cb->skb).sk), 1021a58917f5SEric Dumazet NETLINK_CB(cb->skb).portid, 1022a58917f5SEric Dumazet cb->nlh->nlmsg_seq, NLM_F_MULTI, 1023d545cacaSLorenzo Colitti cb->nlh, net_admin); 102467db3e4bSEric Dumazet if (res < 0) 102567db3e4bSEric Dumazet num = num_arr[idx]; 1026a8c2190eSArnaldo Carvalho de Melo } 102767db3e4bSEric Dumazet sock_gen_put(sk_arr[idx]); 1028a8c2190eSArnaldo Carvalho de Melo } 102967db3e4bSEric Dumazet if (res < 0) 103067db3e4bSEric Dumazet break; 1031acffb584SEric Dumazet cond_resched(); 103267db3e4bSEric Dumazet if (accum == SKARR_SZ) { 103367db3e4bSEric Dumazet s_num = num + 1; 103467db3e4bSEric Dumazet goto next_chunk; 103567db3e4bSEric Dumazet } 1036a8c2190eSArnaldo Carvalho de Melo } 1037a8c2190eSArnaldo Carvalho de Melo 1038a8c2190eSArnaldo Carvalho de Melo done: 1039a8c2190eSArnaldo Carvalho de Melo cb->args[1] = i; 1040a8c2190eSArnaldo Carvalho de Melo cb->args[2] = num; 1041efb3cb42SPavel Emelyanov out: 1042efb3cb42SPavel Emelyanov ; 1043efb3cb42SPavel Emelyanov } 10441942c518SPavel Emelyanov EXPORT_SYMBOL_GPL(inet_diag_dump_icsk); 1045efb3cb42SPavel Emelyanov 1046efb3cb42SPavel Emelyanov static int __inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, 104734160ea3SEric Dumazet const struct inet_diag_req_v2 *r, 1048e31c5e0eSEric Dumazet struct nlattr *bc) 1049efb3cb42SPavel Emelyanov { 1050efb3cb42SPavel Emelyanov const struct inet_diag_handler *handler; 1051cacb6ba0SCyrill Gorcunov int err = 0; 1052efb3cb42SPavel Emelyanov 1053efb3cb42SPavel Emelyanov handler = inet_diag_lock_handler(r->sdiag_protocol); 1054efb3cb42SPavel Emelyanov if (!IS_ERR(handler)) 10551942c518SPavel Emelyanov handler->dump(skb, cb, r, bc); 1056cacb6ba0SCyrill Gorcunov else 1057cacb6ba0SCyrill Gorcunov err = PTR_ERR(handler); 1058d523a328SHerbert Xu inet_diag_unlock_handler(handler); 1059efb3cb42SPavel Emelyanov 1060cacb6ba0SCyrill Gorcunov return err ? : skb->len; 1061a8c2190eSArnaldo Carvalho de Melo } 1062a8c2190eSArnaldo Carvalho de Melo 106325c4cd2bSPavel Emelyanov static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) 106425c4cd2bSPavel Emelyanov { 1065c8991362SPavel Emelyanov int hdrlen = sizeof(struct inet_diag_req_v2); 1066e31c5e0eSEric Dumazet struct nlattr *bc = NULL; 106725c4cd2bSPavel Emelyanov 106825c4cd2bSPavel Emelyanov if (nlmsg_attrlen(cb->nlh, hdrlen)) 106925c4cd2bSPavel Emelyanov bc = nlmsg_find_attr(cb->nlh, hdrlen, INET_DIAG_REQ_BYTECODE); 107025c4cd2bSPavel Emelyanov 1071d106352dSDavid S. Miller return __inet_diag_dump(skb, cb, nlmsg_data(cb->nlh), bc); 107225c4cd2bSPavel Emelyanov } 107325c4cd2bSPavel Emelyanov 1074e31c5e0eSEric Dumazet static int inet_diag_type2proto(int type) 1075a029fe26SPavel Emelyanov { 1076a029fe26SPavel Emelyanov switch (type) { 1077a029fe26SPavel Emelyanov case TCPDIAG_GETSOCK: 1078a029fe26SPavel Emelyanov return IPPROTO_TCP; 1079a029fe26SPavel Emelyanov case DCCPDIAG_GETSOCK: 1080a029fe26SPavel Emelyanov return IPPROTO_DCCP; 1081a029fe26SPavel Emelyanov default: 1082a029fe26SPavel Emelyanov return 0; 1083a029fe26SPavel Emelyanov } 1084a029fe26SPavel Emelyanov } 1085a029fe26SPavel Emelyanov 1086e31c5e0eSEric Dumazet static int inet_diag_dump_compat(struct sk_buff *skb, 1087e31c5e0eSEric Dumazet struct netlink_callback *cb) 108825c4cd2bSPavel Emelyanov { 1089d106352dSDavid S. Miller struct inet_diag_req *rc = nlmsg_data(cb->nlh); 1090e31c5e0eSEric Dumazet int hdrlen = sizeof(struct inet_diag_req); 1091c8991362SPavel Emelyanov struct inet_diag_req_v2 req; 109225c4cd2bSPavel Emelyanov struct nlattr *bc = NULL; 109325c4cd2bSPavel Emelyanov 1094d23deaa0SPavel Emelyanov req.sdiag_family = AF_UNSPEC; /* compatibility */ 109525c4cd2bSPavel Emelyanov req.sdiag_protocol = inet_diag_type2proto(cb->nlh->nlmsg_type); 109625c4cd2bSPavel Emelyanov req.idiag_ext = rc->idiag_ext; 109725c4cd2bSPavel Emelyanov req.idiag_states = rc->idiag_states; 109825c4cd2bSPavel Emelyanov req.id = rc->id; 109925c4cd2bSPavel Emelyanov 110025c4cd2bSPavel Emelyanov if (nlmsg_attrlen(cb->nlh, hdrlen)) 110125c4cd2bSPavel Emelyanov bc = nlmsg_find_attr(cb->nlh, hdrlen, INET_DIAG_REQ_BYTECODE); 110225c4cd2bSPavel Emelyanov 110325c4cd2bSPavel Emelyanov return __inet_diag_dump(skb, cb, &req, bc); 110425c4cd2bSPavel Emelyanov } 110525c4cd2bSPavel Emelyanov 1106fe50ce28SPavel Emelyanov static int inet_diag_get_exact_compat(struct sk_buff *in_skb, 1107fe50ce28SPavel Emelyanov const struct nlmsghdr *nlh) 1108fe50ce28SPavel Emelyanov { 1109d106352dSDavid S. Miller struct inet_diag_req *rc = nlmsg_data(nlh); 1110c8991362SPavel Emelyanov struct inet_diag_req_v2 req; 1111fe50ce28SPavel Emelyanov 1112fe50ce28SPavel Emelyanov req.sdiag_family = rc->idiag_family; 1113fe50ce28SPavel Emelyanov req.sdiag_protocol = inet_diag_type2proto(nlh->nlmsg_type); 1114fe50ce28SPavel Emelyanov req.idiag_ext = rc->idiag_ext; 1115fe50ce28SPavel Emelyanov req.idiag_states = rc->idiag_states; 1116fe50ce28SPavel Emelyanov req.id = rc->id; 1117fe50ce28SPavel Emelyanov 11186eb5d2e0SLorenzo Colitti return inet_diag_cmd_exact(SOCK_DIAG_BY_FAMILY, in_skb, nlh, &req); 1119fe50ce28SPavel Emelyanov } 1120fe50ce28SPavel Emelyanov 11218d34172dSPavel Emelyanov static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh) 1122a8c2190eSArnaldo Carvalho de Melo { 11233b09c84cSPavel Emelyanov int hdrlen = sizeof(struct inet_diag_req); 112451d7cccfSAndrey Vagin struct net *net = sock_net(skb->sk); 1125a8c2190eSArnaldo Carvalho de Melo 1126ead592baSThomas Graf if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX || 1127ead592baSThomas Graf nlmsg_len(nlh) < hdrlen) 1128ead592baSThomas Graf return -EINVAL; 1129a8c2190eSArnaldo Carvalho de Melo 1130b8f3ab42SDavid S. Miller if (nlh->nlmsg_flags & NLM_F_DUMP) { 1131ead592baSThomas Graf if (nlmsg_attrlen(nlh, hdrlen)) { 1132ead592baSThomas Graf struct nlattr *attr; 1133627cc4adSLorenzo Colitti int err; 1134ead592baSThomas Graf 1135ead592baSThomas Graf attr = nlmsg_find_attr(nlh, hdrlen, 1136ead592baSThomas Graf INET_DIAG_REQ_BYTECODE); 1137a52e95abSLorenzo Colitti err = inet_diag_bc_audit(attr, skb); 1138627cc4adSLorenzo Colitti if (err) 1139627cc4adSLorenzo Colitti return err; 1140a8c2190eSArnaldo Carvalho de Melo } 114180d326faSPablo Neira Ayuso { 114280d326faSPablo Neira Ayuso struct netlink_dump_control c = { 114380d326faSPablo Neira Ayuso .dump = inet_diag_dump_compat, 114480d326faSPablo Neira Ayuso }; 114551d7cccfSAndrey Vagin return netlink_dump_start(net->diag_nlsk, skb, nlh, &c); 114680d326faSPablo Neira Ayuso } 1147a8c2190eSArnaldo Carvalho de Melo } 1148ead592baSThomas Graf 1149fe50ce28SPavel Emelyanov return inet_diag_get_exact_compat(skb, nlh); 1150a8c2190eSArnaldo Carvalho de Melo } 1151a8c2190eSArnaldo Carvalho de Melo 11526eb5d2e0SLorenzo Colitti static int inet_diag_handler_cmd(struct sk_buff *skb, struct nlmsghdr *h) 1153d366477aSPavel Emelyanov { 1154c8991362SPavel Emelyanov int hdrlen = sizeof(struct inet_diag_req_v2); 115551d7cccfSAndrey Vagin struct net *net = sock_net(skb->sk); 1156d366477aSPavel Emelyanov 1157d366477aSPavel Emelyanov if (nlmsg_len(h) < hdrlen) 1158d366477aSPavel Emelyanov return -EINVAL; 1159d366477aSPavel Emelyanov 11606eb5d2e0SLorenzo Colitti if (h->nlmsg_type == SOCK_DIAG_BY_FAMILY && 11616eb5d2e0SLorenzo Colitti h->nlmsg_flags & NLM_F_DUMP) { 116225c4cd2bSPavel Emelyanov if (nlmsg_attrlen(h, hdrlen)) { 116325c4cd2bSPavel Emelyanov struct nlattr *attr; 1164627cc4adSLorenzo Colitti int err; 1165e31c5e0eSEric Dumazet 116625c4cd2bSPavel Emelyanov attr = nlmsg_find_attr(h, hdrlen, 116725c4cd2bSPavel Emelyanov INET_DIAG_REQ_BYTECODE); 1168a52e95abSLorenzo Colitti err = inet_diag_bc_audit(attr, skb); 1169627cc4adSLorenzo Colitti if (err) 1170627cc4adSLorenzo Colitti return err; 117125c4cd2bSPavel Emelyanov } 117280d326faSPablo Neira Ayuso { 117380d326faSPablo Neira Ayuso struct netlink_dump_control c = { 117480d326faSPablo Neira Ayuso .dump = inet_diag_dump, 117580d326faSPablo Neira Ayuso }; 117651d7cccfSAndrey Vagin return netlink_dump_start(net->diag_nlsk, skb, h, &c); 117780d326faSPablo Neira Ayuso } 1178d366477aSPavel Emelyanov } 1179d366477aSPavel Emelyanov 11806eb5d2e0SLorenzo Colitti return inet_diag_cmd_exact(h->nlmsg_type, skb, h, nlmsg_data(h)); 1181d366477aSPavel Emelyanov } 1182d366477aSPavel Emelyanov 118335ac838aSCraig Gallek static 118435ac838aSCraig Gallek int inet_diag_handler_get_info(struct sk_buff *skb, struct sock *sk) 118535ac838aSCraig Gallek { 118635ac838aSCraig Gallek const struct inet_diag_handler *handler; 118735ac838aSCraig Gallek struct nlmsghdr *nlh; 118835ac838aSCraig Gallek struct nlattr *attr; 118935ac838aSCraig Gallek struct inet_diag_msg *r; 119035ac838aSCraig Gallek void *info = NULL; 119135ac838aSCraig Gallek int err = 0; 119235ac838aSCraig Gallek 119335ac838aSCraig Gallek nlh = nlmsg_put(skb, 0, 0, SOCK_DIAG_BY_FAMILY, sizeof(*r), 0); 119435ac838aSCraig Gallek if (!nlh) 119535ac838aSCraig Gallek return -ENOMEM; 119635ac838aSCraig Gallek 119735ac838aSCraig Gallek r = nlmsg_data(nlh); 119835ac838aSCraig Gallek memset(r, 0, sizeof(*r)); 119935ac838aSCraig Gallek inet_diag_msg_common_fill(r, sk); 1200e0df02e0SCraig Gallek if (sk->sk_type == SOCK_DGRAM || sk->sk_type == SOCK_STREAM) 1201e0df02e0SCraig Gallek r->id.idiag_sport = inet_sk(sk)->inet_sport; 120235ac838aSCraig Gallek r->idiag_state = sk->sk_state; 120335ac838aSCraig Gallek 120435ac838aSCraig Gallek if ((err = nla_put_u8(skb, INET_DIAG_PROTOCOL, sk->sk_protocol))) { 120535ac838aSCraig Gallek nlmsg_cancel(skb, nlh); 120635ac838aSCraig Gallek return err; 120735ac838aSCraig Gallek } 120835ac838aSCraig Gallek 120935ac838aSCraig Gallek handler = inet_diag_lock_handler(sk->sk_protocol); 121035ac838aSCraig Gallek if (IS_ERR(handler)) { 121135ac838aSCraig Gallek inet_diag_unlock_handler(handler); 121235ac838aSCraig Gallek nlmsg_cancel(skb, nlh); 121335ac838aSCraig Gallek return PTR_ERR(handler); 121435ac838aSCraig Gallek } 121535ac838aSCraig Gallek 121635ac838aSCraig Gallek attr = handler->idiag_info_size 12176ed46d12SNicolas Dichtel ? nla_reserve_64bit(skb, INET_DIAG_INFO, 12186ed46d12SNicolas Dichtel handler->idiag_info_size, 12196ed46d12SNicolas Dichtel INET_DIAG_PAD) 122035ac838aSCraig Gallek : NULL; 122135ac838aSCraig Gallek if (attr) 122235ac838aSCraig Gallek info = nla_data(attr); 122335ac838aSCraig Gallek 122435ac838aSCraig Gallek handler->idiag_get_info(sk, r, info); 122535ac838aSCraig Gallek inet_diag_unlock_handler(handler); 122635ac838aSCraig Gallek 122735ac838aSCraig Gallek nlmsg_end(skb, nlh); 122835ac838aSCraig Gallek return 0; 122935ac838aSCraig Gallek } 123035ac838aSCraig Gallek 12318dcf01fcSShan Wei static const struct sock_diag_handler inet_diag_handler = { 1232d366477aSPavel Emelyanov .family = AF_INET, 12336eb5d2e0SLorenzo Colitti .dump = inet_diag_handler_cmd, 123435ac838aSCraig Gallek .get_info = inet_diag_handler_get_info, 12356eb5d2e0SLorenzo Colitti .destroy = inet_diag_handler_cmd, 1236d366477aSPavel Emelyanov }; 1237d366477aSPavel Emelyanov 12388dcf01fcSShan Wei static const struct sock_diag_handler inet6_diag_handler = { 1239d366477aSPavel Emelyanov .family = AF_INET6, 12406eb5d2e0SLorenzo Colitti .dump = inet_diag_handler_cmd, 124135ac838aSCraig Gallek .get_info = inet_diag_handler_get_info, 12426eb5d2e0SLorenzo Colitti .destroy = inet_diag_handler_cmd, 1243d366477aSPavel Emelyanov }; 1244d366477aSPavel Emelyanov 1245a8c2190eSArnaldo Carvalho de Melo int inet_diag_register(const struct inet_diag_handler *h) 1246a8c2190eSArnaldo Carvalho de Melo { 1247a8c2190eSArnaldo Carvalho de Melo const __u16 type = h->idiag_type; 1248a8c2190eSArnaldo Carvalho de Melo int err = -EINVAL; 1249a8c2190eSArnaldo Carvalho de Melo 1250f13c95f0SPavel Emelyanov if (type >= IPPROTO_MAX) 1251a8c2190eSArnaldo Carvalho de Melo goto out; 1252a8c2190eSArnaldo Carvalho de Melo 1253d523a328SHerbert Xu mutex_lock(&inet_diag_table_mutex); 1254a8c2190eSArnaldo Carvalho de Melo err = -EEXIST; 1255e31c5e0eSEric Dumazet if (!inet_diag_table[type]) { 1256a8c2190eSArnaldo Carvalho de Melo inet_diag_table[type] = h; 1257a8c2190eSArnaldo Carvalho de Melo err = 0; 1258a8c2190eSArnaldo Carvalho de Melo } 1259d523a328SHerbert Xu mutex_unlock(&inet_diag_table_mutex); 1260a8c2190eSArnaldo Carvalho de Melo out: 1261a8c2190eSArnaldo Carvalho de Melo return err; 1262a8c2190eSArnaldo Carvalho de Melo } 1263a8c2190eSArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(inet_diag_register); 1264a8c2190eSArnaldo Carvalho de Melo 1265a8c2190eSArnaldo Carvalho de Melo void inet_diag_unregister(const struct inet_diag_handler *h) 1266a8c2190eSArnaldo Carvalho de Melo { 1267a8c2190eSArnaldo Carvalho de Melo const __u16 type = h->idiag_type; 1268a8c2190eSArnaldo Carvalho de Melo 1269f13c95f0SPavel Emelyanov if (type >= IPPROTO_MAX) 1270a8c2190eSArnaldo Carvalho de Melo return; 1271a8c2190eSArnaldo Carvalho de Melo 1272d523a328SHerbert Xu mutex_lock(&inet_diag_table_mutex); 1273a8c2190eSArnaldo Carvalho de Melo inet_diag_table[type] = NULL; 1274d523a328SHerbert Xu mutex_unlock(&inet_diag_table_mutex); 1275a8c2190eSArnaldo Carvalho de Melo } 1276a8c2190eSArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(inet_diag_unregister); 1277a8c2190eSArnaldo Carvalho de Melo 1278a8c2190eSArnaldo Carvalho de Melo static int __init inet_diag_init(void) 1279a8c2190eSArnaldo Carvalho de Melo { 1280f13c95f0SPavel Emelyanov const int inet_diag_table_size = (IPPROTO_MAX * 1281a8c2190eSArnaldo Carvalho de Melo sizeof(struct inet_diag_handler *)); 1282a8c2190eSArnaldo Carvalho de Melo int err = -ENOMEM; 1283a8c2190eSArnaldo Carvalho de Melo 12840da974f4SPanagiotis Issaris inet_diag_table = kzalloc(inet_diag_table_size, GFP_KERNEL); 1285a8c2190eSArnaldo Carvalho de Melo if (!inet_diag_table) 1286a8c2190eSArnaldo Carvalho de Melo goto out; 1287a8c2190eSArnaldo Carvalho de Melo 1288d366477aSPavel Emelyanov err = sock_diag_register(&inet_diag_handler); 1289d366477aSPavel Emelyanov if (err) 1290d366477aSPavel Emelyanov goto out_free_nl; 1291d366477aSPavel Emelyanov 1292d366477aSPavel Emelyanov err = sock_diag_register(&inet6_diag_handler); 1293d366477aSPavel Emelyanov if (err) 1294d366477aSPavel Emelyanov goto out_free_inet; 1295d366477aSPavel Emelyanov 12968ef874bfSPavel Emelyanov sock_diag_register_inet_compat(inet_diag_rcv_msg_compat); 1297a8c2190eSArnaldo Carvalho de Melo out: 1298a8c2190eSArnaldo Carvalho de Melo return err; 1299d366477aSPavel Emelyanov 1300d366477aSPavel Emelyanov out_free_inet: 1301d366477aSPavel Emelyanov sock_diag_unregister(&inet_diag_handler); 1302d366477aSPavel Emelyanov out_free_nl: 1303a8c2190eSArnaldo Carvalho de Melo kfree(inet_diag_table); 1304a8c2190eSArnaldo Carvalho de Melo goto out; 1305a8c2190eSArnaldo Carvalho de Melo } 1306a8c2190eSArnaldo Carvalho de Melo 1307a8c2190eSArnaldo Carvalho de Melo static void __exit inet_diag_exit(void) 1308a8c2190eSArnaldo Carvalho de Melo { 1309d366477aSPavel Emelyanov sock_diag_unregister(&inet6_diag_handler); 1310d366477aSPavel Emelyanov sock_diag_unregister(&inet_diag_handler); 13118ef874bfSPavel Emelyanov sock_diag_unregister_inet_compat(inet_diag_rcv_msg_compat); 1312a8c2190eSArnaldo Carvalho de Melo kfree(inet_diag_table); 1313a8c2190eSArnaldo Carvalho de Melo } 1314a8c2190eSArnaldo Carvalho de Melo 1315a8c2190eSArnaldo Carvalho de Melo module_init(inet_diag_init); 1316a8c2190eSArnaldo Carvalho de Melo module_exit(inet_diag_exit); 1317a8c2190eSArnaldo Carvalho de Melo MODULE_LICENSE("GPL"); 1318aec8dc62SPavel Emelyanov MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2 /* AF_INET */); 1319aec8dc62SPavel Emelyanov MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 10 /* AF_INET6 */); 1320