1a8c2190eSArnaldo Carvalho de Melo /* 2a8c2190eSArnaldo Carvalho de Melo * inet_diag.c Module for monitoring INET transport protocols sockets. 3a8c2190eSArnaldo Carvalho de Melo * 4a8c2190eSArnaldo Carvalho de Melo * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 5a8c2190eSArnaldo Carvalho de Melo * 6a8c2190eSArnaldo Carvalho de Melo * This program is free software; you can redistribute it and/or 7a8c2190eSArnaldo Carvalho de Melo * modify it under the terms of the GNU General Public License 8a8c2190eSArnaldo Carvalho de Melo * as published by the Free Software Foundation; either version 9a8c2190eSArnaldo Carvalho de Melo * 2 of the License, or (at your option) any later version. 10a8c2190eSArnaldo Carvalho de Melo */ 11a8c2190eSArnaldo Carvalho de Melo 12172589ccSIlpo Järvinen #include <linux/kernel.h> 13a8c2190eSArnaldo Carvalho de Melo #include <linux/module.h> 14a8c2190eSArnaldo Carvalho de Melo #include <linux/types.h> 15a8c2190eSArnaldo Carvalho de Melo #include <linux/fcntl.h> 16a8c2190eSArnaldo Carvalho de Melo #include <linux/random.h> 175a0e3ad6STejun Heo #include <linux/slab.h> 18a8c2190eSArnaldo Carvalho de Melo #include <linux/cache.h> 19a8c2190eSArnaldo Carvalho de Melo #include <linux/init.h> 20a8c2190eSArnaldo Carvalho de Melo #include <linux/time.h> 21a8c2190eSArnaldo Carvalho de Melo 22a8c2190eSArnaldo Carvalho de Melo #include <net/icmp.h> 23a8c2190eSArnaldo Carvalho de Melo #include <net/tcp.h> 24a8c2190eSArnaldo Carvalho de Melo #include <net/ipv6.h> 25a8c2190eSArnaldo Carvalho de Melo #include <net/inet_common.h> 26a8c2190eSArnaldo Carvalho de Melo #include <net/inet_connection_sock.h> 27a8c2190eSArnaldo Carvalho de Melo #include <net/inet_hashtables.h> 28a8c2190eSArnaldo Carvalho de Melo #include <net/inet_timewait_sock.h> 29a8c2190eSArnaldo Carvalho de Melo #include <net/inet6_hashtables.h> 30dc5fc579SArnaldo Carvalho de Melo #include <net/netlink.h> 31a8c2190eSArnaldo Carvalho de Melo 32a8c2190eSArnaldo Carvalho de Melo #include <linux/inet.h> 33a8c2190eSArnaldo Carvalho de Melo #include <linux/stddef.h> 34a8c2190eSArnaldo Carvalho de Melo 35a8c2190eSArnaldo Carvalho de Melo #include <linux/inet_diag.h> 36d366477aSPavel Emelyanov #include <linux/sock_diag.h> 37a8c2190eSArnaldo Carvalho de Melo 38a8c2190eSArnaldo Carvalho de Melo static const struct inet_diag_handler **inet_diag_table; 39a8c2190eSArnaldo Carvalho de Melo 40a8c2190eSArnaldo Carvalho de Melo struct inet_diag_entry { 419f855299SAl Viro __be32 *saddr; 429f855299SAl Viro __be32 *daddr; 43a8c2190eSArnaldo Carvalho de Melo u16 sport; 44a8c2190eSArnaldo Carvalho de Melo u16 dport; 45a8c2190eSArnaldo Carvalho de Melo u16 family; 46a8c2190eSArnaldo Carvalho de Melo u16 userlocks; 47a8c2190eSArnaldo Carvalho de Melo }; 48a8c2190eSArnaldo Carvalho de Melo 49d523a328SHerbert Xu static DEFINE_MUTEX(inet_diag_table_mutex); 50d523a328SHerbert Xu 51f13c95f0SPavel Emelyanov static const struct inet_diag_handler *inet_diag_lock_handler(int proto) 52d523a328SHerbert Xu { 53f13c95f0SPavel Emelyanov if (!inet_diag_table[proto]) 54aec8dc62SPavel Emelyanov request_module("net-pf-%d-proto-%d-type-%d-%d", PF_NETLINK, 55aec8dc62SPavel Emelyanov NETLINK_SOCK_DIAG, AF_INET, proto); 56d523a328SHerbert Xu 57d523a328SHerbert Xu mutex_lock(&inet_diag_table_mutex); 58f13c95f0SPavel Emelyanov if (!inet_diag_table[proto]) 59d523a328SHerbert Xu return ERR_PTR(-ENOENT); 60d523a328SHerbert Xu 61f13c95f0SPavel Emelyanov return inet_diag_table[proto]; 62d523a328SHerbert Xu } 63d523a328SHerbert Xu 64d523a328SHerbert Xu static inline void inet_diag_unlock_handler( 65d523a328SHerbert Xu const struct inet_diag_handler *handler) 66d523a328SHerbert Xu { 67d523a328SHerbert Xu mutex_unlock(&inet_diag_table_mutex); 68d523a328SHerbert Xu } 69d523a328SHerbert Xu 703c4d05c8SPavel Emelyanov int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, 71c8991362SPavel Emelyanov struct sk_buff *skb, struct inet_diag_req_v2 *req, 72d06ca956SEric W. Biederman struct user_namespace *user_ns, 7315e47304SEric W. Biederman u32 portid, u32 seq, u16 nlmsg_flags, 74a8c2190eSArnaldo Carvalho de Melo const struct nlmsghdr *unlh) 75a8c2190eSArnaldo Carvalho de Melo { 76a8c2190eSArnaldo Carvalho de Melo const struct inet_sock *inet = inet_sk(sk); 77a8c2190eSArnaldo Carvalho de Melo struct inet_diag_msg *r; 78a8c2190eSArnaldo Carvalho de Melo struct nlmsghdr *nlh; 796e277ed5SThomas Graf struct nlattr *attr; 80a8c2190eSArnaldo Carvalho de Melo void *info = NULL; 81a8c2190eSArnaldo Carvalho de Melo const struct inet_diag_handler *handler; 82a029fe26SPavel Emelyanov int ext = req->idiag_ext; 83a8c2190eSArnaldo Carvalho de Melo 84a029fe26SPavel Emelyanov handler = inet_diag_table[req->sdiag_protocol]; 85a8c2190eSArnaldo Carvalho de Melo BUG_ON(handler == NULL); 86a8c2190eSArnaldo Carvalho de Melo 8715e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r), 886e277ed5SThomas Graf nlmsg_flags); 896e277ed5SThomas Graf if (!nlh) 90d106352dSDavid S. Miller return -EMSGSIZE; 91a8c2190eSArnaldo Carvalho de Melo 92d106352dSDavid S. Miller r = nlmsg_data(nlh); 93c7d58aabSArnaldo Carvalho de Melo BUG_ON(sk->sk_state == TCP_TIME_WAIT); 94c7d58aabSArnaldo Carvalho de Melo 95a8c2190eSArnaldo Carvalho de Melo r->idiag_family = sk->sk_family; 96a8c2190eSArnaldo Carvalho de Melo r->idiag_state = sk->sk_state; 97a8c2190eSArnaldo Carvalho de Melo r->idiag_timer = 0; 98a8c2190eSArnaldo Carvalho de Melo r->idiag_retrans = 0; 99a8c2190eSArnaldo Carvalho de Melo 100a8c2190eSArnaldo Carvalho de Melo r->id.idiag_if = sk->sk_bound_dev_if; 101f65c1b53SPavel Emelyanov sock_diag_save_cookie(sk, r->id.idiag_cookie); 102a8c2190eSArnaldo Carvalho de Melo 103c720c7e8SEric Dumazet r->id.idiag_sport = inet->inet_sport; 104c720c7e8SEric Dumazet r->id.idiag_dport = inet->inet_dport; 105c720c7e8SEric Dumazet r->id.idiag_src[0] = inet->inet_rcv_saddr; 106c720c7e8SEric Dumazet r->id.idiag_dst[0] = inet->inet_daddr; 107a8c2190eSArnaldo Carvalho de Melo 108*e4e541a8SPavel Emelyanov if (nla_put_u8(skb, INET_DIAG_SHUTDOWN, sk->sk_shutdown)) 109*e4e541a8SPavel Emelyanov goto errout; 110*e4e541a8SPavel Emelyanov 111717b6d83SMaciej Żenczykowski /* IPv6 dual-stack sockets use inet->tos for IPv4 connections, 112717b6d83SMaciej Żenczykowski * hence this needs to be included regardless of socket family. 113717b6d83SMaciej Żenczykowski */ 114717b6d83SMaciej Żenczykowski if (ext & (1 << (INET_DIAG_TOS - 1))) 1156e277ed5SThomas Graf if (nla_put_u8(skb, INET_DIAG_TOS, inet->tos) < 0) 1166e277ed5SThomas Graf goto errout; 117717b6d83SMaciej Żenczykowski 118dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 119a8c2190eSArnaldo Carvalho de Melo if (r->idiag_family == AF_INET6) { 120b71d1d42SEric Dumazet const struct ipv6_pinfo *np = inet6_sk(sk); 121a8c2190eSArnaldo Carvalho de Melo 1224e3fd7a0SAlexey Dobriyan *(struct in6_addr *)r->id.idiag_src = np->rcv_saddr; 1234e3fd7a0SAlexey Dobriyan *(struct in6_addr *)r->id.idiag_dst = np->daddr; 1246e277ed5SThomas Graf 12506236ac3SMaciej Żenczykowski if (ext & (1 << (INET_DIAG_TCLASS - 1))) 1266e277ed5SThomas Graf if (nla_put_u8(skb, INET_DIAG_TCLASS, np->tclass) < 0) 1276e277ed5SThomas Graf goto errout; 128a8c2190eSArnaldo Carvalho de Melo } 129a8c2190eSArnaldo Carvalho de Melo #endif 130a8c2190eSArnaldo Carvalho de Melo 131d06ca956SEric W. Biederman r->idiag_uid = from_kuid_munged(user_ns, sock_i_uid(sk)); 1323c4d05c8SPavel Emelyanov r->idiag_inode = sock_i_ino(sk); 1333c4d05c8SPavel Emelyanov 1346e277ed5SThomas Graf if (ext & (1 << (INET_DIAG_MEMINFO - 1))) { 1356e277ed5SThomas Graf struct inet_diag_meminfo minfo = { 1366e277ed5SThomas Graf .idiag_rmem = sk_rmem_alloc_get(sk), 1376e277ed5SThomas Graf .idiag_wmem = sk->sk_wmem_queued, 1386e277ed5SThomas Graf .idiag_fmem = sk->sk_forward_alloc, 1396e277ed5SThomas Graf .idiag_tmem = sk_wmem_alloc_get(sk), 1406e277ed5SThomas Graf }; 1416e277ed5SThomas Graf 1426e277ed5SThomas Graf if (nla_put(skb, INET_DIAG_MEMINFO, sizeof(minfo), &minfo) < 0) 1436e277ed5SThomas Graf goto errout; 1443c4d05c8SPavel Emelyanov } 1453c4d05c8SPavel Emelyanov 146c0636faaSPavel Emelyanov if (ext & (1 << (INET_DIAG_SKMEMINFO - 1))) 147c0636faaSPavel Emelyanov if (sock_diag_put_meminfo(sk, skb, INET_DIAG_SKMEMINFO)) 1486e277ed5SThomas Graf goto errout; 149c0636faaSPavel Emelyanov 1503c4d05c8SPavel Emelyanov if (icsk == NULL) { 15162ad6fcdSShan Wei handler->idiag_get_info(sk, r, NULL); 1523c4d05c8SPavel Emelyanov goto out; 1533c4d05c8SPavel Emelyanov } 1543c4d05c8SPavel Emelyanov 155172589ccSIlpo Järvinen #define EXPIRES_IN_MS(tmo) DIV_ROUND_UP((tmo - jiffies) * 1000, HZ) 156a8c2190eSArnaldo Carvalho de Melo 157a8c2190eSArnaldo Carvalho de Melo if (icsk->icsk_pending == ICSK_TIME_RETRANS) { 158a8c2190eSArnaldo Carvalho de Melo r->idiag_timer = 1; 159a8c2190eSArnaldo Carvalho de Melo r->idiag_retrans = icsk->icsk_retransmits; 160a8c2190eSArnaldo Carvalho de Melo r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout); 161a8c2190eSArnaldo Carvalho de Melo } else if (icsk->icsk_pending == ICSK_TIME_PROBE0) { 162a8c2190eSArnaldo Carvalho de Melo r->idiag_timer = 4; 163a8c2190eSArnaldo Carvalho de Melo r->idiag_retrans = icsk->icsk_probes_out; 164a8c2190eSArnaldo Carvalho de Melo r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout); 165a8c2190eSArnaldo Carvalho de Melo } else if (timer_pending(&sk->sk_timer)) { 166a8c2190eSArnaldo Carvalho de Melo r->idiag_timer = 2; 167a8c2190eSArnaldo Carvalho de Melo r->idiag_retrans = icsk->icsk_probes_out; 168a8c2190eSArnaldo Carvalho de Melo r->idiag_expires = EXPIRES_IN_MS(sk->sk_timer.expires); 169a8c2190eSArnaldo Carvalho de Melo } else { 170a8c2190eSArnaldo Carvalho de Melo r->idiag_timer = 0; 171a8c2190eSArnaldo Carvalho de Melo r->idiag_expires = 0; 172a8c2190eSArnaldo Carvalho de Melo } 173a8c2190eSArnaldo Carvalho de Melo #undef EXPIRES_IN_MS 174a8c2190eSArnaldo Carvalho de Melo 1756e277ed5SThomas Graf if (ext & (1 << (INET_DIAG_INFO - 1))) { 1766e277ed5SThomas Graf attr = nla_reserve(skb, INET_DIAG_INFO, 1776e277ed5SThomas Graf sizeof(struct tcp_info)); 1786e277ed5SThomas Graf if (!attr) 1796e277ed5SThomas Graf goto errout; 180a8c2190eSArnaldo Carvalho de Melo 1816e277ed5SThomas Graf info = nla_data(attr); 182a8c2190eSArnaldo Carvalho de Melo } 183a8c2190eSArnaldo Carvalho de Melo 1846e277ed5SThomas Graf if ((ext & (1 << (INET_DIAG_CONG - 1))) && icsk->icsk_ca_ops) 1856e277ed5SThomas Graf if (nla_put_string(skb, INET_DIAG_CONG, 1866e277ed5SThomas Graf icsk->icsk_ca_ops->name) < 0) 1876e277ed5SThomas Graf goto errout; 1886e277ed5SThomas Graf 189a8c2190eSArnaldo Carvalho de Melo handler->idiag_get_info(sk, r, info); 190a8c2190eSArnaldo Carvalho de Melo 191a8c2190eSArnaldo Carvalho de Melo if (sk->sk_state < TCP_TIME_WAIT && 192a8c2190eSArnaldo Carvalho de Melo icsk->icsk_ca_ops && icsk->icsk_ca_ops->get_info) 193a8c2190eSArnaldo Carvalho de Melo icsk->icsk_ca_ops->get_info(sk, ext, skb); 194a8c2190eSArnaldo Carvalho de Melo 1953c4d05c8SPavel Emelyanov out: 1966e277ed5SThomas Graf return nlmsg_end(skb, nlh); 197a8c2190eSArnaldo Carvalho de Melo 1986e277ed5SThomas Graf errout: 1996e277ed5SThomas Graf nlmsg_cancel(skb, nlh); 20026932566SPatrick McHardy return -EMSGSIZE; 201a8c2190eSArnaldo Carvalho de Melo } 2023c4d05c8SPavel Emelyanov EXPORT_SYMBOL_GPL(inet_sk_diag_fill); 2033c4d05c8SPavel Emelyanov 2043c4d05c8SPavel Emelyanov static int inet_csk_diag_fill(struct sock *sk, 205c8991362SPavel Emelyanov struct sk_buff *skb, struct inet_diag_req_v2 *req, 206d06ca956SEric W. Biederman struct user_namespace *user_ns, 20715e47304SEric W. Biederman u32 portid, u32 seq, u16 nlmsg_flags, 2083c4d05c8SPavel Emelyanov const struct nlmsghdr *unlh) 2093c4d05c8SPavel Emelyanov { 2103c4d05c8SPavel Emelyanov return inet_sk_diag_fill(sk, inet_csk(sk), 21115e47304SEric W. Biederman skb, req, user_ns, portid, seq, nlmsg_flags, unlh); 2123c4d05c8SPavel Emelyanov } 213a8c2190eSArnaldo Carvalho de Melo 214c7d58aabSArnaldo Carvalho de Melo static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, 215c8991362SPavel Emelyanov struct sk_buff *skb, struct inet_diag_req_v2 *req, 21615e47304SEric W. Biederman u32 portid, u32 seq, u16 nlmsg_flags, 217c7d58aabSArnaldo Carvalho de Melo const struct nlmsghdr *unlh) 218c7d58aabSArnaldo Carvalho de Melo { 219c7d58aabSArnaldo Carvalho de Melo long tmo; 220c7d58aabSArnaldo Carvalho de Melo struct inet_diag_msg *r; 2216e277ed5SThomas Graf struct nlmsghdr *nlh; 222c7d58aabSArnaldo Carvalho de Melo 22315e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r), 2246e277ed5SThomas Graf nlmsg_flags); 2256e277ed5SThomas Graf if (!nlh) 226d106352dSDavid S. Miller return -EMSGSIZE; 227d106352dSDavid S. Miller 228d106352dSDavid S. Miller r = nlmsg_data(nlh); 229c7d58aabSArnaldo Carvalho de Melo BUG_ON(tw->tw_state != TCP_TIME_WAIT); 230c7d58aabSArnaldo Carvalho de Melo 231c7d58aabSArnaldo Carvalho de Melo tmo = tw->tw_ttd - jiffies; 232c7d58aabSArnaldo Carvalho de Melo if (tmo < 0) 233c7d58aabSArnaldo Carvalho de Melo tmo = 0; 234c7d58aabSArnaldo Carvalho de Melo 235c7d58aabSArnaldo Carvalho de Melo r->idiag_family = tw->tw_family; 236c7d58aabSArnaldo Carvalho de Melo r->idiag_retrans = 0; 237c7d58aabSArnaldo Carvalho de Melo r->id.idiag_if = tw->tw_bound_dev_if; 238f65c1b53SPavel Emelyanov sock_diag_save_cookie(tw, r->id.idiag_cookie); 239c7d58aabSArnaldo Carvalho de Melo r->id.idiag_sport = tw->tw_sport; 240c7d58aabSArnaldo Carvalho de Melo r->id.idiag_dport = tw->tw_dport; 241c7d58aabSArnaldo Carvalho de Melo r->id.idiag_src[0] = tw->tw_rcv_saddr; 242c7d58aabSArnaldo Carvalho de Melo r->id.idiag_dst[0] = tw->tw_daddr; 243c7d58aabSArnaldo Carvalho de Melo r->idiag_state = tw->tw_substate; 244c7d58aabSArnaldo Carvalho de Melo r->idiag_timer = 3; 245172589ccSIlpo Järvinen r->idiag_expires = DIV_ROUND_UP(tmo * 1000, HZ); 246c7d58aabSArnaldo Carvalho de Melo r->idiag_rqueue = 0; 247c7d58aabSArnaldo Carvalho de Melo r->idiag_wqueue = 0; 248c7d58aabSArnaldo Carvalho de Melo r->idiag_uid = 0; 249c7d58aabSArnaldo Carvalho de Melo r->idiag_inode = 0; 250dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 251c7d58aabSArnaldo Carvalho de Melo if (tw->tw_family == AF_INET6) { 252c7d58aabSArnaldo Carvalho de Melo const struct inet6_timewait_sock *tw6 = 253c7d58aabSArnaldo Carvalho de Melo inet6_twsk((struct sock *)tw); 254c7d58aabSArnaldo Carvalho de Melo 2554e3fd7a0SAlexey Dobriyan *(struct in6_addr *)r->id.idiag_src = tw6->tw_v6_rcv_saddr; 2564e3fd7a0SAlexey Dobriyan *(struct in6_addr *)r->id.idiag_dst = tw6->tw_v6_daddr; 257c7d58aabSArnaldo Carvalho de Melo } 258c7d58aabSArnaldo Carvalho de Melo #endif 2596e277ed5SThomas Graf 2606e277ed5SThomas Graf return nlmsg_end(skb, nlh); 261c7d58aabSArnaldo Carvalho de Melo } 262c7d58aabSArnaldo Carvalho de Melo 263dff2c035SArnaldo Carvalho de Melo static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, 264d06ca956SEric W. Biederman struct inet_diag_req_v2 *r, 265d06ca956SEric W. Biederman struct user_namespace *user_ns, 26615e47304SEric W. Biederman u32 portid, u32 seq, u16 nlmsg_flags, 267dff2c035SArnaldo Carvalho de Melo const struct nlmsghdr *unlh) 268dff2c035SArnaldo Carvalho de Melo { 269dff2c035SArnaldo Carvalho de Melo if (sk->sk_state == TCP_TIME_WAIT) 270dff2c035SArnaldo Carvalho de Melo return inet_twsk_diag_fill((struct inet_timewait_sock *)sk, 27115e47304SEric W. Biederman skb, r, portid, seq, nlmsg_flags, 272dff2c035SArnaldo Carvalho de Melo unlh); 27315e47304SEric W. Biederman return inet_csk_diag_fill(sk, skb, r, user_ns, portid, seq, nlmsg_flags, unlh); 274dff2c035SArnaldo Carvalho de Melo } 275dff2c035SArnaldo Carvalho de Melo 2761942c518SPavel Emelyanov int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_skb, 277c8991362SPavel Emelyanov const struct nlmsghdr *nlh, struct inet_diag_req_v2 *req) 278a8c2190eSArnaldo Carvalho de Melo { 279a8c2190eSArnaldo Carvalho de Melo int err; 280a8c2190eSArnaldo Carvalho de Melo struct sock *sk; 281a8c2190eSArnaldo Carvalho de Melo struct sk_buff *rep; 28251d7cccfSAndrey Vagin struct net *net = sock_net(in_skb->sk); 283a8c2190eSArnaldo Carvalho de Melo 284d523a328SHerbert Xu err = -EINVAL; 285fe50ce28SPavel Emelyanov if (req->sdiag_family == AF_INET) { 28651d7cccfSAndrey Vagin sk = inet_lookup(net, hashinfo, req->id.idiag_dst[0], 287a8c2190eSArnaldo Carvalho de Melo req->id.idiag_dport, req->id.idiag_src[0], 288a8c2190eSArnaldo Carvalho de Melo req->id.idiag_sport, req->id.idiag_if); 289a8c2190eSArnaldo Carvalho de Melo } 290dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 291fe50ce28SPavel Emelyanov else if (req->sdiag_family == AF_INET6) { 29251d7cccfSAndrey Vagin sk = inet6_lookup(net, hashinfo, 293a8c2190eSArnaldo Carvalho de Melo (struct in6_addr *)req->id.idiag_dst, 294a8c2190eSArnaldo Carvalho de Melo req->id.idiag_dport, 295a8c2190eSArnaldo Carvalho de Melo (struct in6_addr *)req->id.idiag_src, 296a8c2190eSArnaldo Carvalho de Melo req->id.idiag_sport, 297a8c2190eSArnaldo Carvalho de Melo req->id.idiag_if); 298a8c2190eSArnaldo Carvalho de Melo } 299a8c2190eSArnaldo Carvalho de Melo #endif 300a8c2190eSArnaldo Carvalho de Melo else { 301476f7dbfSPavel Emelyanov goto out_nosk; 302a8c2190eSArnaldo Carvalho de Melo } 303a8c2190eSArnaldo Carvalho de Melo 304d523a328SHerbert Xu err = -ENOENT; 305a8c2190eSArnaldo Carvalho de Melo if (sk == NULL) 306476f7dbfSPavel Emelyanov goto out_nosk; 307a8c2190eSArnaldo Carvalho de Melo 308f65c1b53SPavel Emelyanov err = sock_diag_check_cookie(sk, req->id.idiag_cookie); 309b005ab4eSPavel Emelyanov if (err) 310a8c2190eSArnaldo Carvalho de Melo goto out; 311a8c2190eSArnaldo Carvalho de Melo 3126e277ed5SThomas Graf rep = nlmsg_new(sizeof(struct inet_diag_msg) + 313a8c2190eSArnaldo Carvalho de Melo sizeof(struct inet_diag_meminfo) + 3146e277ed5SThomas Graf sizeof(struct tcp_info) + 64, GFP_KERNEL); 3156e277ed5SThomas Graf if (!rep) { 3166e277ed5SThomas Graf err = -ENOMEM; 317a8c2190eSArnaldo Carvalho de Melo goto out; 3186e277ed5SThomas Graf } 319a8c2190eSArnaldo Carvalho de Melo 320a029fe26SPavel Emelyanov err = sk_diag_fill(sk, rep, req, 321d06ca956SEric W. Biederman sk_user_ns(NETLINK_CB(in_skb).ssk), 32215e47304SEric W. Biederman NETLINK_CB(in_skb).portid, 32326932566SPatrick McHardy nlh->nlmsg_seq, 0, nlh); 32426932566SPatrick McHardy if (err < 0) { 32526932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 3266e277ed5SThomas Graf nlmsg_free(rep); 32726932566SPatrick McHardy goto out; 32826932566SPatrick McHardy } 32915e47304SEric W. Biederman err = netlink_unicast(net->diag_nlsk, rep, NETLINK_CB(in_skb).portid, 330a8c2190eSArnaldo Carvalho de Melo MSG_DONTWAIT); 331a8c2190eSArnaldo Carvalho de Melo if (err > 0) 332a8c2190eSArnaldo Carvalho de Melo err = 0; 333a8c2190eSArnaldo Carvalho de Melo 334a8c2190eSArnaldo Carvalho de Melo out: 335a8c2190eSArnaldo Carvalho de Melo if (sk) { 336a8c2190eSArnaldo Carvalho de Melo if (sk->sk_state == TCP_TIME_WAIT) 337a8c2190eSArnaldo Carvalho de Melo inet_twsk_put((struct inet_timewait_sock *)sk); 338a8c2190eSArnaldo Carvalho de Melo else 339a8c2190eSArnaldo Carvalho de Melo sock_put(sk); 340a8c2190eSArnaldo Carvalho de Melo } 341476f7dbfSPavel Emelyanov out_nosk: 342476f7dbfSPavel Emelyanov return err; 343476f7dbfSPavel Emelyanov } 3441942c518SPavel Emelyanov EXPORT_SYMBOL_GPL(inet_diag_dump_one_icsk); 345476f7dbfSPavel Emelyanov 346476f7dbfSPavel Emelyanov static int inet_diag_get_exact(struct sk_buff *in_skb, 347476f7dbfSPavel Emelyanov const struct nlmsghdr *nlh, 348c8991362SPavel Emelyanov struct inet_diag_req_v2 *req) 349476f7dbfSPavel Emelyanov { 350476f7dbfSPavel Emelyanov const struct inet_diag_handler *handler; 351476f7dbfSPavel Emelyanov int err; 352476f7dbfSPavel Emelyanov 353476f7dbfSPavel Emelyanov handler = inet_diag_lock_handler(req->sdiag_protocol); 354476f7dbfSPavel Emelyanov if (IS_ERR(handler)) 355476f7dbfSPavel Emelyanov err = PTR_ERR(handler); 356476f7dbfSPavel Emelyanov else 3571942c518SPavel Emelyanov err = handler->dump_one(in_skb, nlh, req); 358d523a328SHerbert Xu inet_diag_unlock_handler(handler); 359476f7dbfSPavel Emelyanov 360a8c2190eSArnaldo Carvalho de Melo return err; 361a8c2190eSArnaldo Carvalho de Melo } 362a8c2190eSArnaldo Carvalho de Melo 3639f855299SAl Viro static int bitstring_match(const __be32 *a1, const __be32 *a2, int bits) 364a8c2190eSArnaldo Carvalho de Melo { 365a8c2190eSArnaldo Carvalho de Melo int words = bits >> 5; 366a8c2190eSArnaldo Carvalho de Melo 367a8c2190eSArnaldo Carvalho de Melo bits &= 0x1f; 368a8c2190eSArnaldo Carvalho de Melo 369a8c2190eSArnaldo Carvalho de Melo if (words) { 370a8c2190eSArnaldo Carvalho de Melo if (memcmp(a1, a2, words << 2)) 371a8c2190eSArnaldo Carvalho de Melo return 0; 372a8c2190eSArnaldo Carvalho de Melo } 373a8c2190eSArnaldo Carvalho de Melo if (bits) { 3749f855299SAl Viro __be32 w1, w2; 3759f855299SAl Viro __be32 mask; 376a8c2190eSArnaldo Carvalho de Melo 377a8c2190eSArnaldo Carvalho de Melo w1 = a1[words]; 378a8c2190eSArnaldo Carvalho de Melo w2 = a2[words]; 379a8c2190eSArnaldo Carvalho de Melo 380a8c2190eSArnaldo Carvalho de Melo mask = htonl((0xffffffff) << (32 - bits)); 381a8c2190eSArnaldo Carvalho de Melo 382a8c2190eSArnaldo Carvalho de Melo if ((w1 ^ w2) & mask) 383a8c2190eSArnaldo Carvalho de Melo return 0; 384a8c2190eSArnaldo Carvalho de Melo } 385a8c2190eSArnaldo Carvalho de Melo 386a8c2190eSArnaldo Carvalho de Melo return 1; 387a8c2190eSArnaldo Carvalho de Melo } 388a8c2190eSArnaldo Carvalho de Melo 389a8c2190eSArnaldo Carvalho de Melo 39087c22ea5SPavel Emelyanov static int inet_diag_bc_run(const struct nlattr *_bc, 391a8c2190eSArnaldo Carvalho de Melo const struct inet_diag_entry *entry) 392a8c2190eSArnaldo Carvalho de Melo { 39387c22ea5SPavel Emelyanov const void *bc = nla_data(_bc); 39487c22ea5SPavel Emelyanov int len = nla_len(_bc); 39587c22ea5SPavel Emelyanov 396a8c2190eSArnaldo Carvalho de Melo while (len > 0) { 397a8c2190eSArnaldo Carvalho de Melo int yes = 1; 398a8c2190eSArnaldo Carvalho de Melo const struct inet_diag_bc_op *op = bc; 399a8c2190eSArnaldo Carvalho de Melo 400a8c2190eSArnaldo Carvalho de Melo switch (op->code) { 401a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_NOP: 402a8c2190eSArnaldo Carvalho de Melo break; 403a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_JMP: 404a8c2190eSArnaldo Carvalho de Melo yes = 0; 405a8c2190eSArnaldo Carvalho de Melo break; 406a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_S_GE: 407a8c2190eSArnaldo Carvalho de Melo yes = entry->sport >= op[1].no; 408a8c2190eSArnaldo Carvalho de Melo break; 409a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_S_LE: 410b4ced2b7SRoel Kluin yes = entry->sport <= op[1].no; 411a8c2190eSArnaldo Carvalho de Melo break; 412a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_D_GE: 413a8c2190eSArnaldo Carvalho de Melo yes = entry->dport >= op[1].no; 414a8c2190eSArnaldo Carvalho de Melo break; 415a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_D_LE: 416a8c2190eSArnaldo Carvalho de Melo yes = entry->dport <= op[1].no; 417a8c2190eSArnaldo Carvalho de Melo break; 418a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_AUTO: 419a8c2190eSArnaldo Carvalho de Melo yes = !(entry->userlocks & SOCK_BINDPORT_LOCK); 420a8c2190eSArnaldo Carvalho de Melo break; 421a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_S_COND: 422a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_D_COND: { 423a8c2190eSArnaldo Carvalho de Melo struct inet_diag_hostcond *cond; 4249f855299SAl Viro __be32 *addr; 425a8c2190eSArnaldo Carvalho de Melo 426a8c2190eSArnaldo Carvalho de Melo cond = (struct inet_diag_hostcond *)(op + 1); 427a8c2190eSArnaldo Carvalho de Melo if (cond->port != -1 && 428a8c2190eSArnaldo Carvalho de Melo cond->port != (op->code == INET_DIAG_BC_S_COND ? 429a8c2190eSArnaldo Carvalho de Melo entry->sport : entry->dport)) { 430a8c2190eSArnaldo Carvalho de Melo yes = 0; 431a8c2190eSArnaldo Carvalho de Melo break; 432a8c2190eSArnaldo Carvalho de Melo } 433a8c2190eSArnaldo Carvalho de Melo 434a8c2190eSArnaldo Carvalho de Melo if (cond->prefix_len == 0) 435a8c2190eSArnaldo Carvalho de Melo break; 436a8c2190eSArnaldo Carvalho de Melo 437a8c2190eSArnaldo Carvalho de Melo if (op->code == INET_DIAG_BC_S_COND) 438a8c2190eSArnaldo Carvalho de Melo addr = entry->saddr; 439a8c2190eSArnaldo Carvalho de Melo else 440a8c2190eSArnaldo Carvalho de Melo addr = entry->daddr; 441a8c2190eSArnaldo Carvalho de Melo 4424e852c02SArnaldo Carvalho de Melo if (bitstring_match(addr, cond->addr, 4434e852c02SArnaldo Carvalho de Melo cond->prefix_len)) 444a8c2190eSArnaldo Carvalho de Melo break; 445a8c2190eSArnaldo Carvalho de Melo if (entry->family == AF_INET6 && 446a8c2190eSArnaldo Carvalho de Melo cond->family == AF_INET) { 447a8c2190eSArnaldo Carvalho de Melo if (addr[0] == 0 && addr[1] == 0 && 448a8c2190eSArnaldo Carvalho de Melo addr[2] == htonl(0xffff) && 449a8c2190eSArnaldo Carvalho de Melo bitstring_match(addr + 3, cond->addr, 450a8c2190eSArnaldo Carvalho de Melo cond->prefix_len)) 451a8c2190eSArnaldo Carvalho de Melo break; 452a8c2190eSArnaldo Carvalho de Melo } 453a8c2190eSArnaldo Carvalho de Melo yes = 0; 454a8c2190eSArnaldo Carvalho de Melo break; 455a8c2190eSArnaldo Carvalho de Melo } 456a8c2190eSArnaldo Carvalho de Melo } 457a8c2190eSArnaldo Carvalho de Melo 458a8c2190eSArnaldo Carvalho de Melo if (yes) { 459a8c2190eSArnaldo Carvalho de Melo len -= op->yes; 460a8c2190eSArnaldo Carvalho de Melo bc += op->yes; 461a8c2190eSArnaldo Carvalho de Melo } else { 462a8c2190eSArnaldo Carvalho de Melo len -= op->no; 463a8c2190eSArnaldo Carvalho de Melo bc += op->no; 464a8c2190eSArnaldo Carvalho de Melo } 465a8c2190eSArnaldo Carvalho de Melo } 466a02cec21SEric Dumazet return len == 0; 467a8c2190eSArnaldo Carvalho de Melo } 468a8c2190eSArnaldo Carvalho de Melo 4698d07d151SPavel Emelyanov int inet_diag_bc_sk(const struct nlattr *bc, struct sock *sk) 4708d07d151SPavel Emelyanov { 4718d07d151SPavel Emelyanov struct inet_diag_entry entry; 4728d07d151SPavel Emelyanov struct inet_sock *inet = inet_sk(sk); 4738d07d151SPavel Emelyanov 4748d07d151SPavel Emelyanov if (bc == NULL) 4758d07d151SPavel Emelyanov return 1; 4768d07d151SPavel Emelyanov 4778d07d151SPavel Emelyanov entry.family = sk->sk_family; 478dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 4798d07d151SPavel Emelyanov if (entry.family == AF_INET6) { 4808d07d151SPavel Emelyanov struct ipv6_pinfo *np = inet6_sk(sk); 4818d07d151SPavel Emelyanov 4828d07d151SPavel Emelyanov entry.saddr = np->rcv_saddr.s6_addr32; 4838d07d151SPavel Emelyanov entry.daddr = np->daddr.s6_addr32; 4848d07d151SPavel Emelyanov } else 4858d07d151SPavel Emelyanov #endif 4868d07d151SPavel Emelyanov { 4878d07d151SPavel Emelyanov entry.saddr = &inet->inet_rcv_saddr; 4888d07d151SPavel Emelyanov entry.daddr = &inet->inet_daddr; 4898d07d151SPavel Emelyanov } 4908d07d151SPavel Emelyanov entry.sport = inet->inet_num; 4918d07d151SPavel Emelyanov entry.dport = ntohs(inet->inet_dport); 4928d07d151SPavel Emelyanov entry.userlocks = sk->sk_userlocks; 4938d07d151SPavel Emelyanov 4948d07d151SPavel Emelyanov return inet_diag_bc_run(bc, &entry); 4958d07d151SPavel Emelyanov } 4968d07d151SPavel Emelyanov EXPORT_SYMBOL_GPL(inet_diag_bc_sk); 4978d07d151SPavel Emelyanov 498a8c2190eSArnaldo Carvalho de Melo static int valid_cc(const void *bc, int len, int cc) 499a8c2190eSArnaldo Carvalho de Melo { 500a8c2190eSArnaldo Carvalho de Melo while (len >= 0) { 501a8c2190eSArnaldo Carvalho de Melo const struct inet_diag_bc_op *op = bc; 502a8c2190eSArnaldo Carvalho de Melo 503a8c2190eSArnaldo Carvalho de Melo if (cc > len) 504a8c2190eSArnaldo Carvalho de Melo return 0; 505a8c2190eSArnaldo Carvalho de Melo if (cc == len) 506a8c2190eSArnaldo Carvalho de Melo return 1; 507eeb14972SEric Dumazet if (op->yes < 4 || op->yes & 3) 508a8c2190eSArnaldo Carvalho de Melo return 0; 509a8c2190eSArnaldo Carvalho de Melo len -= op->yes; 510a8c2190eSArnaldo Carvalho de Melo bc += op->yes; 511a8c2190eSArnaldo Carvalho de Melo } 512a8c2190eSArnaldo Carvalho de Melo return 0; 513a8c2190eSArnaldo Carvalho de Melo } 514a8c2190eSArnaldo Carvalho de Melo 515a8c2190eSArnaldo Carvalho de Melo static int inet_diag_bc_audit(const void *bytecode, int bytecode_len) 516a8c2190eSArnaldo Carvalho de Melo { 517eeb14972SEric Dumazet const void *bc = bytecode; 518a8c2190eSArnaldo Carvalho de Melo int len = bytecode_len; 519a8c2190eSArnaldo Carvalho de Melo 520a8c2190eSArnaldo Carvalho de Melo while (len > 0) { 521eeb14972SEric Dumazet const struct inet_diag_bc_op *op = bc; 522a8c2190eSArnaldo Carvalho de Melo 523a8c2190eSArnaldo Carvalho de Melo //printk("BC: %d %d %d {%d} / %d\n", op->code, op->yes, op->no, op[1].no, len); 524a8c2190eSArnaldo Carvalho de Melo switch (op->code) { 525a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_AUTO: 526a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_S_COND: 527a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_D_COND: 528a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_S_GE: 529a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_S_LE: 530a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_D_GE: 531a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_D_LE: 532a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_JMP: 533eeb14972SEric Dumazet if (op->no < 4 || op->no > len + 4 || op->no & 3) 534a8c2190eSArnaldo Carvalho de Melo return -EINVAL; 535a8c2190eSArnaldo Carvalho de Melo if (op->no < len && 536a8c2190eSArnaldo Carvalho de Melo !valid_cc(bytecode, bytecode_len, len - op->no)) 537a8c2190eSArnaldo Carvalho de Melo return -EINVAL; 538a8c2190eSArnaldo Carvalho de Melo break; 539a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_NOP: 540a8c2190eSArnaldo Carvalho de Melo break; 541a8c2190eSArnaldo Carvalho de Melo default: 542a8c2190eSArnaldo Carvalho de Melo return -EINVAL; 543a8c2190eSArnaldo Carvalho de Melo } 544eeb14972SEric Dumazet if (op->yes < 4 || op->yes > len + 4 || op->yes & 3) 545eeb14972SEric Dumazet return -EINVAL; 546a8c2190eSArnaldo Carvalho de Melo bc += op->yes; 547a8c2190eSArnaldo Carvalho de Melo len -= op->yes; 548a8c2190eSArnaldo Carvalho de Melo } 549a8c2190eSArnaldo Carvalho de Melo return len == 0 ? 0 : -EINVAL; 550a8c2190eSArnaldo Carvalho de Melo } 551a8c2190eSArnaldo Carvalho de Melo 552dff2c035SArnaldo Carvalho de Melo static int inet_csk_diag_dump(struct sock *sk, 553dff2c035SArnaldo Carvalho de Melo struct sk_buff *skb, 55437f352b5SPavel Emelyanov struct netlink_callback *cb, 555c8991362SPavel Emelyanov struct inet_diag_req_v2 *r, 55637f352b5SPavel Emelyanov const struct nlattr *bc) 557a8c2190eSArnaldo Carvalho de Melo { 5588d07d151SPavel Emelyanov if (!inet_diag_bc_sk(bc, sk)) 559a8c2190eSArnaldo Carvalho de Melo return 0; 560a8c2190eSArnaldo Carvalho de Melo 561a029fe26SPavel Emelyanov return inet_csk_diag_fill(sk, skb, r, 562d06ca956SEric W. Biederman sk_user_ns(NETLINK_CB(cb->skb).ssk), 56315e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 564a8c2190eSArnaldo Carvalho de Melo cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); 565a8c2190eSArnaldo Carvalho de Melo } 566a8c2190eSArnaldo Carvalho de Melo 567c7d58aabSArnaldo Carvalho de Melo static int inet_twsk_diag_dump(struct inet_timewait_sock *tw, 568c7d58aabSArnaldo Carvalho de Melo struct sk_buff *skb, 56937f352b5SPavel Emelyanov struct netlink_callback *cb, 570c8991362SPavel Emelyanov struct inet_diag_req_v2 *r, 57137f352b5SPavel Emelyanov const struct nlattr *bc) 572c7d58aabSArnaldo Carvalho de Melo { 57337f352b5SPavel Emelyanov if (bc != NULL) { 574c7d58aabSArnaldo Carvalho de Melo struct inet_diag_entry entry; 575c7d58aabSArnaldo Carvalho de Melo 576c7d58aabSArnaldo Carvalho de Melo entry.family = tw->tw_family; 577dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 578c7d58aabSArnaldo Carvalho de Melo if (tw->tw_family == AF_INET6) { 579c7d58aabSArnaldo Carvalho de Melo struct inet6_timewait_sock *tw6 = 580c7d58aabSArnaldo Carvalho de Melo inet6_twsk((struct sock *)tw); 581c7d58aabSArnaldo Carvalho de Melo entry.saddr = tw6->tw_v6_rcv_saddr.s6_addr32; 582c7d58aabSArnaldo Carvalho de Melo entry.daddr = tw6->tw_v6_daddr.s6_addr32; 583c7d58aabSArnaldo Carvalho de Melo } else 584c7d58aabSArnaldo Carvalho de Melo #endif 585c7d58aabSArnaldo Carvalho de Melo { 586c7d58aabSArnaldo Carvalho de Melo entry.saddr = &tw->tw_rcv_saddr; 587c7d58aabSArnaldo Carvalho de Melo entry.daddr = &tw->tw_daddr; 588c7d58aabSArnaldo Carvalho de Melo } 589c7d58aabSArnaldo Carvalho de Melo entry.sport = tw->tw_num; 590c7d58aabSArnaldo Carvalho de Melo entry.dport = ntohs(tw->tw_dport); 591c7d58aabSArnaldo Carvalho de Melo entry.userlocks = 0; 592c7d58aabSArnaldo Carvalho de Melo 59387c22ea5SPavel Emelyanov if (!inet_diag_bc_run(bc, &entry)) 594c7d58aabSArnaldo Carvalho de Melo return 0; 595c7d58aabSArnaldo Carvalho de Melo } 596c7d58aabSArnaldo Carvalho de Melo 597a029fe26SPavel Emelyanov return inet_twsk_diag_fill(tw, skb, r, 59815e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 599c7d58aabSArnaldo Carvalho de Melo cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); 600c7d58aabSArnaldo Carvalho de Melo } 601c7d58aabSArnaldo Carvalho de Melo 602a8c2190eSArnaldo Carvalho de Melo static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, 603d06ca956SEric W. Biederman struct request_sock *req, 604d06ca956SEric W. Biederman struct user_namespace *user_ns, 60515e47304SEric W. Biederman u32 portid, u32 seq, 606a8c2190eSArnaldo Carvalho de Melo const struct nlmsghdr *unlh) 607a8c2190eSArnaldo Carvalho de Melo { 608a8c2190eSArnaldo Carvalho de Melo const struct inet_request_sock *ireq = inet_rsk(req); 609a8c2190eSArnaldo Carvalho de Melo struct inet_sock *inet = inet_sk(sk); 610a8c2190eSArnaldo Carvalho de Melo struct inet_diag_msg *r; 611a8c2190eSArnaldo Carvalho de Melo struct nlmsghdr *nlh; 612a8c2190eSArnaldo Carvalho de Melo long tmo; 613a8c2190eSArnaldo Carvalho de Melo 61415e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r), 6156e277ed5SThomas Graf NLM_F_MULTI); 6166e277ed5SThomas Graf if (!nlh) 6176e277ed5SThomas Graf return -EMSGSIZE; 618a8c2190eSArnaldo Carvalho de Melo 6196e277ed5SThomas Graf r = nlmsg_data(nlh); 620a8c2190eSArnaldo Carvalho de Melo r->idiag_family = sk->sk_family; 621a8c2190eSArnaldo Carvalho de Melo r->idiag_state = TCP_SYN_RECV; 622a8c2190eSArnaldo Carvalho de Melo r->idiag_timer = 1; 623a8c2190eSArnaldo Carvalho de Melo r->idiag_retrans = req->retrans; 624a8c2190eSArnaldo Carvalho de Melo 625a8c2190eSArnaldo Carvalho de Melo r->id.idiag_if = sk->sk_bound_dev_if; 626f65c1b53SPavel Emelyanov sock_diag_save_cookie(req, r->id.idiag_cookie); 627a8c2190eSArnaldo Carvalho de Melo 628a8c2190eSArnaldo Carvalho de Melo tmo = req->expires - jiffies; 629a8c2190eSArnaldo Carvalho de Melo if (tmo < 0) 630a8c2190eSArnaldo Carvalho de Melo tmo = 0; 631a8c2190eSArnaldo Carvalho de Melo 632c720c7e8SEric Dumazet r->id.idiag_sport = inet->inet_sport; 633a8c2190eSArnaldo Carvalho de Melo r->id.idiag_dport = ireq->rmt_port; 634a8c2190eSArnaldo Carvalho de Melo r->id.idiag_src[0] = ireq->loc_addr; 635a8c2190eSArnaldo Carvalho de Melo r->id.idiag_dst[0] = ireq->rmt_addr; 636a8c2190eSArnaldo Carvalho de Melo r->idiag_expires = jiffies_to_msecs(tmo); 637a8c2190eSArnaldo Carvalho de Melo r->idiag_rqueue = 0; 638a8c2190eSArnaldo Carvalho de Melo r->idiag_wqueue = 0; 639d06ca956SEric W. Biederman r->idiag_uid = from_kuid_munged(user_ns, sock_i_uid(sk)); 640a8c2190eSArnaldo Carvalho de Melo r->idiag_inode = 0; 641dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 642a8c2190eSArnaldo Carvalho de Melo if (r->idiag_family == AF_INET6) { 6434e3fd7a0SAlexey Dobriyan *(struct in6_addr *)r->id.idiag_src = inet6_rsk(req)->loc_addr; 6444e3fd7a0SAlexey Dobriyan *(struct in6_addr *)r->id.idiag_dst = inet6_rsk(req)->rmt_addr; 645a8c2190eSArnaldo Carvalho de Melo } 646a8c2190eSArnaldo Carvalho de Melo #endif 647a8c2190eSArnaldo Carvalho de Melo 6486e277ed5SThomas Graf return nlmsg_end(skb, nlh); 649a8c2190eSArnaldo Carvalho de Melo } 650a8c2190eSArnaldo Carvalho de Melo 651a8c2190eSArnaldo Carvalho de Melo static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk, 65237f352b5SPavel Emelyanov struct netlink_callback *cb, 653c8991362SPavel Emelyanov struct inet_diag_req_v2 *r, 65437f352b5SPavel Emelyanov const struct nlattr *bc) 655a8c2190eSArnaldo Carvalho de Melo { 656a8c2190eSArnaldo Carvalho de Melo struct inet_diag_entry entry; 657a8c2190eSArnaldo Carvalho de Melo struct inet_connection_sock *icsk = inet_csk(sk); 658a8c2190eSArnaldo Carvalho de Melo struct listen_sock *lopt; 659a8c2190eSArnaldo Carvalho de Melo struct inet_sock *inet = inet_sk(sk); 660a8c2190eSArnaldo Carvalho de Melo int j, s_j; 661a8c2190eSArnaldo Carvalho de Melo int reqnum, s_reqnum; 662a8c2190eSArnaldo Carvalho de Melo int err = 0; 663a8c2190eSArnaldo Carvalho de Melo 664a8c2190eSArnaldo Carvalho de Melo s_j = cb->args[3]; 665a8c2190eSArnaldo Carvalho de Melo s_reqnum = cb->args[4]; 666a8c2190eSArnaldo Carvalho de Melo 667a8c2190eSArnaldo Carvalho de Melo if (s_j > 0) 668a8c2190eSArnaldo Carvalho de Melo s_j--; 669a8c2190eSArnaldo Carvalho de Melo 670a8c2190eSArnaldo Carvalho de Melo entry.family = sk->sk_family; 671a8c2190eSArnaldo Carvalho de Melo 672a8c2190eSArnaldo Carvalho de Melo read_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock); 673a8c2190eSArnaldo Carvalho de Melo 674a8c2190eSArnaldo Carvalho de Melo lopt = icsk->icsk_accept_queue.listen_opt; 675a8c2190eSArnaldo Carvalho de Melo if (!lopt || !lopt->qlen) 676a8c2190eSArnaldo Carvalho de Melo goto out; 677a8c2190eSArnaldo Carvalho de Melo 67837f352b5SPavel Emelyanov if (bc != NULL) { 679c720c7e8SEric Dumazet entry.sport = inet->inet_num; 680a8c2190eSArnaldo Carvalho de Melo entry.userlocks = sk->sk_userlocks; 681a8c2190eSArnaldo Carvalho de Melo } 682a8c2190eSArnaldo Carvalho de Melo 683a8c2190eSArnaldo Carvalho de Melo for (j = s_j; j < lopt->nr_table_entries; j++) { 684a8c2190eSArnaldo Carvalho de Melo struct request_sock *req, *head = lopt->syn_table[j]; 685a8c2190eSArnaldo Carvalho de Melo 686a8c2190eSArnaldo Carvalho de Melo reqnum = 0; 687a8c2190eSArnaldo Carvalho de Melo for (req = head; req; reqnum++, req = req->dl_next) { 688a8c2190eSArnaldo Carvalho de Melo struct inet_request_sock *ireq = inet_rsk(req); 689a8c2190eSArnaldo Carvalho de Melo 690a8c2190eSArnaldo Carvalho de Melo if (reqnum < s_reqnum) 691a8c2190eSArnaldo Carvalho de Melo continue; 692a8c2190eSArnaldo Carvalho de Melo if (r->id.idiag_dport != ireq->rmt_port && 693a8c2190eSArnaldo Carvalho de Melo r->id.idiag_dport) 694a8c2190eSArnaldo Carvalho de Melo continue; 695a8c2190eSArnaldo Carvalho de Melo 696a8c2190eSArnaldo Carvalho de Melo if (bc) { 697a8c2190eSArnaldo Carvalho de Melo entry.saddr = 698dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 699a8c2190eSArnaldo Carvalho de Melo (entry.family == AF_INET6) ? 700ca304b61SArnaldo Carvalho de Melo inet6_rsk(req)->loc_addr.s6_addr32 : 701a8c2190eSArnaldo Carvalho de Melo #endif 702a8c2190eSArnaldo Carvalho de Melo &ireq->loc_addr; 703a8c2190eSArnaldo Carvalho de Melo entry.daddr = 704dfd56b8bSEric Dumazet #if IS_ENABLED(CONFIG_IPV6) 705a8c2190eSArnaldo Carvalho de Melo (entry.family == AF_INET6) ? 706ca304b61SArnaldo Carvalho de Melo inet6_rsk(req)->rmt_addr.s6_addr32 : 707a8c2190eSArnaldo Carvalho de Melo #endif 708a8c2190eSArnaldo Carvalho de Melo &ireq->rmt_addr; 709a8c2190eSArnaldo Carvalho de Melo entry.dport = ntohs(ireq->rmt_port); 710a8c2190eSArnaldo Carvalho de Melo 71187c22ea5SPavel Emelyanov if (!inet_diag_bc_run(bc, &entry)) 712a8c2190eSArnaldo Carvalho de Melo continue; 713a8c2190eSArnaldo Carvalho de Melo } 714a8c2190eSArnaldo Carvalho de Melo 715a8c2190eSArnaldo Carvalho de Melo err = inet_diag_fill_req(skb, sk, req, 716d06ca956SEric W. Biederman sk_user_ns(NETLINK_CB(cb->skb).ssk), 71715e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 718a8c2190eSArnaldo Carvalho de Melo cb->nlh->nlmsg_seq, cb->nlh); 719a8c2190eSArnaldo Carvalho de Melo if (err < 0) { 720a8c2190eSArnaldo Carvalho de Melo cb->args[3] = j + 1; 721a8c2190eSArnaldo Carvalho de Melo cb->args[4] = reqnum; 722a8c2190eSArnaldo Carvalho de Melo goto out; 723a8c2190eSArnaldo Carvalho de Melo } 724a8c2190eSArnaldo Carvalho de Melo } 725a8c2190eSArnaldo Carvalho de Melo 726a8c2190eSArnaldo Carvalho de Melo s_reqnum = 0; 727a8c2190eSArnaldo Carvalho de Melo } 728a8c2190eSArnaldo Carvalho de Melo 729a8c2190eSArnaldo Carvalho de Melo out: 730a8c2190eSArnaldo Carvalho de Melo read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock); 731a8c2190eSArnaldo Carvalho de Melo 732a8c2190eSArnaldo Carvalho de Melo return err; 733a8c2190eSArnaldo Carvalho de Melo } 734a8c2190eSArnaldo Carvalho de Melo 7351942c518SPavel Emelyanov void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, 736c8991362SPavel Emelyanov struct netlink_callback *cb, struct inet_diag_req_v2 *r, struct nlattr *bc) 737a8c2190eSArnaldo Carvalho de Melo { 738a8c2190eSArnaldo Carvalho de Melo int i, num; 739a8c2190eSArnaldo Carvalho de Melo int s_i, s_num; 74051d7cccfSAndrey Vagin struct net *net = sock_net(skb->sk); 741a8c2190eSArnaldo Carvalho de Melo 742a8c2190eSArnaldo Carvalho de Melo s_i = cb->args[1]; 743a8c2190eSArnaldo Carvalho de Melo s_num = num = cb->args[2]; 744a8c2190eSArnaldo Carvalho de Melo 745a8c2190eSArnaldo Carvalho de Melo if (cb->args[0] == 0) { 746a8c2190eSArnaldo Carvalho de Melo if (!(r->idiag_states & (TCPF_LISTEN | TCPF_SYN_RECV))) 747a8c2190eSArnaldo Carvalho de Melo goto skip_listen_ht; 748a8c2190eSArnaldo Carvalho de Melo 749a8c2190eSArnaldo Carvalho de Melo for (i = s_i; i < INET_LHTABLE_SIZE; i++) { 750a8c2190eSArnaldo Carvalho de Melo struct sock *sk; 751c25eb3bfSEric Dumazet struct hlist_nulls_node *node; 7525caea4eaSEric Dumazet struct inet_listen_hashbucket *ilb; 753a8c2190eSArnaldo Carvalho de Melo 754a8c2190eSArnaldo Carvalho de Melo num = 0; 7555caea4eaSEric Dumazet ilb = &hashinfo->listening_hash[i]; 7565caea4eaSEric Dumazet spin_lock_bh(&ilb->lock); 757c25eb3bfSEric Dumazet sk_nulls_for_each(sk, node, &ilb->head) { 758a8c2190eSArnaldo Carvalho de Melo struct inet_sock *inet = inet_sk(sk); 759a8c2190eSArnaldo Carvalho de Melo 76051d7cccfSAndrey Vagin if (!net_eq(sock_net(sk), net)) 76151d7cccfSAndrey Vagin continue; 76251d7cccfSAndrey Vagin 763a8c2190eSArnaldo Carvalho de Melo if (num < s_num) { 764a8c2190eSArnaldo Carvalho de Melo num++; 765a8c2190eSArnaldo Carvalho de Melo continue; 766a8c2190eSArnaldo Carvalho de Melo } 767a8c2190eSArnaldo Carvalho de Melo 768d23deaa0SPavel Emelyanov if (r->sdiag_family != AF_UNSPEC && 769d23deaa0SPavel Emelyanov sk->sk_family != r->sdiag_family) 770d23deaa0SPavel Emelyanov goto next_listen; 771d23deaa0SPavel Emelyanov 772c720c7e8SEric Dumazet if (r->id.idiag_sport != inet->inet_sport && 773a8c2190eSArnaldo Carvalho de Melo r->id.idiag_sport) 774a8c2190eSArnaldo Carvalho de Melo goto next_listen; 775a8c2190eSArnaldo Carvalho de Melo 776a8c2190eSArnaldo Carvalho de Melo if (!(r->idiag_states & TCPF_LISTEN) || 777a8c2190eSArnaldo Carvalho de Melo r->id.idiag_dport || 778a8c2190eSArnaldo Carvalho de Melo cb->args[3] > 0) 779a8c2190eSArnaldo Carvalho de Melo goto syn_recv; 780a8c2190eSArnaldo Carvalho de Melo 78125c4cd2bSPavel Emelyanov if (inet_csk_diag_dump(sk, skb, cb, r, bc) < 0) { 7825caea4eaSEric Dumazet spin_unlock_bh(&ilb->lock); 783a8c2190eSArnaldo Carvalho de Melo goto done; 784a8c2190eSArnaldo Carvalho de Melo } 785a8c2190eSArnaldo Carvalho de Melo 786a8c2190eSArnaldo Carvalho de Melo syn_recv: 787a8c2190eSArnaldo Carvalho de Melo if (!(r->idiag_states & TCPF_SYN_RECV)) 788a8c2190eSArnaldo Carvalho de Melo goto next_listen; 789a8c2190eSArnaldo Carvalho de Melo 79025c4cd2bSPavel Emelyanov if (inet_diag_dump_reqs(skb, sk, cb, r, bc) < 0) { 7915caea4eaSEric Dumazet spin_unlock_bh(&ilb->lock); 792a8c2190eSArnaldo Carvalho de Melo goto done; 793a8c2190eSArnaldo Carvalho de Melo } 794a8c2190eSArnaldo Carvalho de Melo 795a8c2190eSArnaldo Carvalho de Melo next_listen: 796a8c2190eSArnaldo Carvalho de Melo cb->args[3] = 0; 797a8c2190eSArnaldo Carvalho de Melo cb->args[4] = 0; 798a8c2190eSArnaldo Carvalho de Melo ++num; 799a8c2190eSArnaldo Carvalho de Melo } 8005caea4eaSEric Dumazet spin_unlock_bh(&ilb->lock); 801a8c2190eSArnaldo Carvalho de Melo 802a8c2190eSArnaldo Carvalho de Melo s_num = 0; 803a8c2190eSArnaldo Carvalho de Melo cb->args[3] = 0; 804a8c2190eSArnaldo Carvalho de Melo cb->args[4] = 0; 805a8c2190eSArnaldo Carvalho de Melo } 806a8c2190eSArnaldo Carvalho de Melo skip_listen_ht: 807a8c2190eSArnaldo Carvalho de Melo cb->args[0] = 1; 808a8c2190eSArnaldo Carvalho de Melo s_i = num = s_num = 0; 809a8c2190eSArnaldo Carvalho de Melo } 810a8c2190eSArnaldo Carvalho de Melo 811a8c2190eSArnaldo Carvalho de Melo if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV))) 812efb3cb42SPavel Emelyanov goto out; 813a8c2190eSArnaldo Carvalho de Melo 814f373b53bSEric Dumazet for (i = s_i; i <= hashinfo->ehash_mask; i++) { 815a8c2190eSArnaldo Carvalho de Melo struct inet_ehash_bucket *head = &hashinfo->ehash[i]; 8167e3aab4aSDavid S. Miller spinlock_t *lock = inet_ehash_lockp(hashinfo, i); 817a8c2190eSArnaldo Carvalho de Melo struct sock *sk; 8183ab5aee7SEric Dumazet struct hlist_nulls_node *node; 819a8c2190eSArnaldo Carvalho de Melo 8206be547a6SAndi Kleen num = 0; 8216be547a6SAndi Kleen 8223ab5aee7SEric Dumazet if (hlist_nulls_empty(&head->chain) && 8233ab5aee7SEric Dumazet hlist_nulls_empty(&head->twchain)) 8246be547a6SAndi Kleen continue; 8256be547a6SAndi Kleen 826a8c2190eSArnaldo Carvalho de Melo if (i > s_i) 827a8c2190eSArnaldo Carvalho de Melo s_num = 0; 828a8c2190eSArnaldo Carvalho de Melo 8297e3aab4aSDavid S. Miller spin_lock_bh(lock); 8303ab5aee7SEric Dumazet sk_nulls_for_each(sk, node, &head->chain) { 831a8c2190eSArnaldo Carvalho de Melo struct inet_sock *inet = inet_sk(sk); 832a8c2190eSArnaldo Carvalho de Melo 83351d7cccfSAndrey Vagin if (!net_eq(sock_net(sk), net)) 83451d7cccfSAndrey Vagin continue; 835a8c2190eSArnaldo Carvalho de Melo if (num < s_num) 836a8c2190eSArnaldo Carvalho de Melo goto next_normal; 837a8c2190eSArnaldo Carvalho de Melo if (!(r->idiag_states & (1 << sk->sk_state))) 838a8c2190eSArnaldo Carvalho de Melo goto next_normal; 839d23deaa0SPavel Emelyanov if (r->sdiag_family != AF_UNSPEC && 840d23deaa0SPavel Emelyanov sk->sk_family != r->sdiag_family) 841d23deaa0SPavel Emelyanov goto next_normal; 842c720c7e8SEric Dumazet if (r->id.idiag_sport != inet->inet_sport && 843a8c2190eSArnaldo Carvalho de Melo r->id.idiag_sport) 844a8c2190eSArnaldo Carvalho de Melo goto next_normal; 845c720c7e8SEric Dumazet if (r->id.idiag_dport != inet->inet_dport && 8464e852c02SArnaldo Carvalho de Melo r->id.idiag_dport) 847a8c2190eSArnaldo Carvalho de Melo goto next_normal; 84825c4cd2bSPavel Emelyanov if (inet_csk_diag_dump(sk, skb, cb, r, bc) < 0) { 8497e3aab4aSDavid S. Miller spin_unlock_bh(lock); 850a8c2190eSArnaldo Carvalho de Melo goto done; 851a8c2190eSArnaldo Carvalho de Melo } 852a8c2190eSArnaldo Carvalho de Melo next_normal: 853a8c2190eSArnaldo Carvalho de Melo ++num; 854a8c2190eSArnaldo Carvalho de Melo } 855a8c2190eSArnaldo Carvalho de Melo 856a8c2190eSArnaldo Carvalho de Melo if (r->idiag_states & TCPF_TIME_WAIT) { 857c7d58aabSArnaldo Carvalho de Melo struct inet_timewait_sock *tw; 858c7d58aabSArnaldo Carvalho de Melo 859c7d58aabSArnaldo Carvalho de Melo inet_twsk_for_each(tw, node, 860dbca9b27SEric Dumazet &head->twchain) { 86151d7cccfSAndrey Vagin if (!net_eq(twsk_net(tw), net)) 86251d7cccfSAndrey Vagin continue; 863a8c2190eSArnaldo Carvalho de Melo 864a8c2190eSArnaldo Carvalho de Melo if (num < s_num) 865a8c2190eSArnaldo Carvalho de Melo goto next_dying; 866d23deaa0SPavel Emelyanov if (r->sdiag_family != AF_UNSPEC && 867d23deaa0SPavel Emelyanov tw->tw_family != r->sdiag_family) 868d23deaa0SPavel Emelyanov goto next_dying; 8697dbf0755SArnaldo Carvalho de Melo if (r->id.idiag_sport != tw->tw_sport && 870a8c2190eSArnaldo Carvalho de Melo r->id.idiag_sport) 871a8c2190eSArnaldo Carvalho de Melo goto next_dying; 8727dbf0755SArnaldo Carvalho de Melo if (r->id.idiag_dport != tw->tw_dport && 873a8c2190eSArnaldo Carvalho de Melo r->id.idiag_dport) 874a8c2190eSArnaldo Carvalho de Melo goto next_dying; 87525c4cd2bSPavel Emelyanov if (inet_twsk_diag_dump(tw, skb, cb, r, bc) < 0) { 8767e3aab4aSDavid S. Miller spin_unlock_bh(lock); 877a8c2190eSArnaldo Carvalho de Melo goto done; 878a8c2190eSArnaldo Carvalho de Melo } 879a8c2190eSArnaldo Carvalho de Melo next_dying: 880a8c2190eSArnaldo Carvalho de Melo ++num; 881a8c2190eSArnaldo Carvalho de Melo } 882a8c2190eSArnaldo Carvalho de Melo } 8837e3aab4aSDavid S. Miller spin_unlock_bh(lock); 884a8c2190eSArnaldo Carvalho de Melo } 885a8c2190eSArnaldo Carvalho de Melo 886a8c2190eSArnaldo Carvalho de Melo done: 887a8c2190eSArnaldo Carvalho de Melo cb->args[1] = i; 888a8c2190eSArnaldo Carvalho de Melo cb->args[2] = num; 889efb3cb42SPavel Emelyanov out: 890efb3cb42SPavel Emelyanov ; 891efb3cb42SPavel Emelyanov } 8921942c518SPavel Emelyanov EXPORT_SYMBOL_GPL(inet_diag_dump_icsk); 893efb3cb42SPavel Emelyanov 894efb3cb42SPavel Emelyanov static int __inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, 895c8991362SPavel Emelyanov struct inet_diag_req_v2 *r, struct nlattr *bc) 896efb3cb42SPavel Emelyanov { 897efb3cb42SPavel Emelyanov const struct inet_diag_handler *handler; 898efb3cb42SPavel Emelyanov 899efb3cb42SPavel Emelyanov handler = inet_diag_lock_handler(r->sdiag_protocol); 900efb3cb42SPavel Emelyanov if (!IS_ERR(handler)) 9011942c518SPavel Emelyanov handler->dump(skb, cb, r, bc); 902d523a328SHerbert Xu inet_diag_unlock_handler(handler); 903efb3cb42SPavel Emelyanov 904a8c2190eSArnaldo Carvalho de Melo return skb->len; 905a8c2190eSArnaldo Carvalho de Melo } 906a8c2190eSArnaldo Carvalho de Melo 90725c4cd2bSPavel Emelyanov static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) 90825c4cd2bSPavel Emelyanov { 90925c4cd2bSPavel Emelyanov struct nlattr *bc = NULL; 910c8991362SPavel Emelyanov int hdrlen = sizeof(struct inet_diag_req_v2); 91125c4cd2bSPavel Emelyanov 91225c4cd2bSPavel Emelyanov if (nlmsg_attrlen(cb->nlh, hdrlen)) 91325c4cd2bSPavel Emelyanov bc = nlmsg_find_attr(cb->nlh, hdrlen, INET_DIAG_REQ_BYTECODE); 91425c4cd2bSPavel Emelyanov 915d106352dSDavid S. Miller return __inet_diag_dump(skb, cb, nlmsg_data(cb->nlh), bc); 91625c4cd2bSPavel Emelyanov } 91725c4cd2bSPavel Emelyanov 918a029fe26SPavel Emelyanov static inline int inet_diag_type2proto(int type) 919a029fe26SPavel Emelyanov { 920a029fe26SPavel Emelyanov switch (type) { 921a029fe26SPavel Emelyanov case TCPDIAG_GETSOCK: 922a029fe26SPavel Emelyanov return IPPROTO_TCP; 923a029fe26SPavel Emelyanov case DCCPDIAG_GETSOCK: 924a029fe26SPavel Emelyanov return IPPROTO_DCCP; 925a029fe26SPavel Emelyanov default: 926a029fe26SPavel Emelyanov return 0; 927a029fe26SPavel Emelyanov } 928a029fe26SPavel Emelyanov } 929a029fe26SPavel Emelyanov 93025c4cd2bSPavel Emelyanov static int inet_diag_dump_compat(struct sk_buff *skb, struct netlink_callback *cb) 93125c4cd2bSPavel Emelyanov { 932d106352dSDavid S. Miller struct inet_diag_req *rc = nlmsg_data(cb->nlh); 933c8991362SPavel Emelyanov struct inet_diag_req_v2 req; 93425c4cd2bSPavel Emelyanov struct nlattr *bc = NULL; 9353b09c84cSPavel Emelyanov int hdrlen = sizeof(struct inet_diag_req); 93625c4cd2bSPavel Emelyanov 937d23deaa0SPavel Emelyanov req.sdiag_family = AF_UNSPEC; /* compatibility */ 93825c4cd2bSPavel Emelyanov req.sdiag_protocol = inet_diag_type2proto(cb->nlh->nlmsg_type); 93925c4cd2bSPavel Emelyanov req.idiag_ext = rc->idiag_ext; 94025c4cd2bSPavel Emelyanov req.idiag_states = rc->idiag_states; 94125c4cd2bSPavel Emelyanov req.id = rc->id; 94225c4cd2bSPavel Emelyanov 94325c4cd2bSPavel Emelyanov if (nlmsg_attrlen(cb->nlh, hdrlen)) 94425c4cd2bSPavel Emelyanov bc = nlmsg_find_attr(cb->nlh, hdrlen, INET_DIAG_REQ_BYTECODE); 94525c4cd2bSPavel Emelyanov 94625c4cd2bSPavel Emelyanov return __inet_diag_dump(skb, cb, &req, bc); 94725c4cd2bSPavel Emelyanov } 94825c4cd2bSPavel Emelyanov 949fe50ce28SPavel Emelyanov static int inet_diag_get_exact_compat(struct sk_buff *in_skb, 950fe50ce28SPavel Emelyanov const struct nlmsghdr *nlh) 951fe50ce28SPavel Emelyanov { 952d106352dSDavid S. Miller struct inet_diag_req *rc = nlmsg_data(nlh); 953c8991362SPavel Emelyanov struct inet_diag_req_v2 req; 954fe50ce28SPavel Emelyanov 955fe50ce28SPavel Emelyanov req.sdiag_family = rc->idiag_family; 956fe50ce28SPavel Emelyanov req.sdiag_protocol = inet_diag_type2proto(nlh->nlmsg_type); 957fe50ce28SPavel Emelyanov req.idiag_ext = rc->idiag_ext; 958fe50ce28SPavel Emelyanov req.idiag_states = rc->idiag_states; 959fe50ce28SPavel Emelyanov req.id = rc->id; 960fe50ce28SPavel Emelyanov 961fe50ce28SPavel Emelyanov return inet_diag_get_exact(in_skb, nlh, &req); 962fe50ce28SPavel Emelyanov } 963fe50ce28SPavel Emelyanov 9648d34172dSPavel Emelyanov static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh) 965a8c2190eSArnaldo Carvalho de Melo { 9663b09c84cSPavel Emelyanov int hdrlen = sizeof(struct inet_diag_req); 96751d7cccfSAndrey Vagin struct net *net = sock_net(skb->sk); 968a8c2190eSArnaldo Carvalho de Melo 969ead592baSThomas Graf if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX || 970ead592baSThomas Graf nlmsg_len(nlh) < hdrlen) 971ead592baSThomas Graf return -EINVAL; 972a8c2190eSArnaldo Carvalho de Melo 973b8f3ab42SDavid S. Miller if (nlh->nlmsg_flags & NLM_F_DUMP) { 974ead592baSThomas Graf if (nlmsg_attrlen(nlh, hdrlen)) { 975ead592baSThomas Graf struct nlattr *attr; 976ead592baSThomas Graf 977ead592baSThomas Graf attr = nlmsg_find_attr(nlh, hdrlen, 978ead592baSThomas Graf INET_DIAG_REQ_BYTECODE); 979ead592baSThomas Graf if (attr == NULL || 980ead592baSThomas Graf nla_len(attr) < sizeof(struct inet_diag_bc_op) || 981ead592baSThomas Graf inet_diag_bc_audit(nla_data(attr), nla_len(attr))) 982a8c2190eSArnaldo Carvalho de Melo return -EINVAL; 983a8c2190eSArnaldo Carvalho de Melo } 98480d326faSPablo Neira Ayuso { 98580d326faSPablo Neira Ayuso struct netlink_dump_control c = { 98680d326faSPablo Neira Ayuso .dump = inet_diag_dump_compat, 98780d326faSPablo Neira Ayuso }; 98851d7cccfSAndrey Vagin return netlink_dump_start(net->diag_nlsk, skb, nlh, &c); 98980d326faSPablo Neira Ayuso } 990a8c2190eSArnaldo Carvalho de Melo } 991ead592baSThomas Graf 992fe50ce28SPavel Emelyanov return inet_diag_get_exact_compat(skb, nlh); 993a8c2190eSArnaldo Carvalho de Melo } 994a8c2190eSArnaldo Carvalho de Melo 995d366477aSPavel Emelyanov static int inet_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h) 996d366477aSPavel Emelyanov { 997c8991362SPavel Emelyanov int hdrlen = sizeof(struct inet_diag_req_v2); 99851d7cccfSAndrey Vagin struct net *net = sock_net(skb->sk); 999d366477aSPavel Emelyanov 1000d366477aSPavel Emelyanov if (nlmsg_len(h) < hdrlen) 1001d366477aSPavel Emelyanov return -EINVAL; 1002d366477aSPavel Emelyanov 1003d366477aSPavel Emelyanov if (h->nlmsg_flags & NLM_F_DUMP) { 100425c4cd2bSPavel Emelyanov if (nlmsg_attrlen(h, hdrlen)) { 100525c4cd2bSPavel Emelyanov struct nlattr *attr; 100625c4cd2bSPavel Emelyanov attr = nlmsg_find_attr(h, hdrlen, 100725c4cd2bSPavel Emelyanov INET_DIAG_REQ_BYTECODE); 100825c4cd2bSPavel Emelyanov if (attr == NULL || 100925c4cd2bSPavel Emelyanov nla_len(attr) < sizeof(struct inet_diag_bc_op) || 101025c4cd2bSPavel Emelyanov inet_diag_bc_audit(nla_data(attr), nla_len(attr))) 101125c4cd2bSPavel Emelyanov return -EINVAL; 101225c4cd2bSPavel Emelyanov } 101380d326faSPablo Neira Ayuso { 101480d326faSPablo Neira Ayuso struct netlink_dump_control c = { 101580d326faSPablo Neira Ayuso .dump = inet_diag_dump, 101680d326faSPablo Neira Ayuso }; 101751d7cccfSAndrey Vagin return netlink_dump_start(net->diag_nlsk, skb, h, &c); 101880d326faSPablo Neira Ayuso } 1019d366477aSPavel Emelyanov } 1020d366477aSPavel Emelyanov 1021d106352dSDavid S. Miller return inet_diag_get_exact(skb, h, nlmsg_data(h)); 1022d366477aSPavel Emelyanov } 1023d366477aSPavel Emelyanov 10248dcf01fcSShan Wei static const struct sock_diag_handler inet_diag_handler = { 1025d366477aSPavel Emelyanov .family = AF_INET, 1026d366477aSPavel Emelyanov .dump = inet_diag_handler_dump, 1027d366477aSPavel Emelyanov }; 1028d366477aSPavel Emelyanov 10298dcf01fcSShan Wei static const struct sock_diag_handler inet6_diag_handler = { 1030d366477aSPavel Emelyanov .family = AF_INET6, 1031d366477aSPavel Emelyanov .dump = inet_diag_handler_dump, 1032d366477aSPavel Emelyanov }; 1033d366477aSPavel Emelyanov 1034a8c2190eSArnaldo Carvalho de Melo int inet_diag_register(const struct inet_diag_handler *h) 1035a8c2190eSArnaldo Carvalho de Melo { 1036a8c2190eSArnaldo Carvalho de Melo const __u16 type = h->idiag_type; 1037a8c2190eSArnaldo Carvalho de Melo int err = -EINVAL; 1038a8c2190eSArnaldo Carvalho de Melo 1039f13c95f0SPavel Emelyanov if (type >= IPPROTO_MAX) 1040a8c2190eSArnaldo Carvalho de Melo goto out; 1041a8c2190eSArnaldo Carvalho de Melo 1042d523a328SHerbert Xu mutex_lock(&inet_diag_table_mutex); 1043a8c2190eSArnaldo Carvalho de Melo err = -EEXIST; 1044a8c2190eSArnaldo Carvalho de Melo if (inet_diag_table[type] == NULL) { 1045a8c2190eSArnaldo Carvalho de Melo inet_diag_table[type] = h; 1046a8c2190eSArnaldo Carvalho de Melo err = 0; 1047a8c2190eSArnaldo Carvalho de Melo } 1048d523a328SHerbert Xu mutex_unlock(&inet_diag_table_mutex); 1049a8c2190eSArnaldo Carvalho de Melo out: 1050a8c2190eSArnaldo Carvalho de Melo return err; 1051a8c2190eSArnaldo Carvalho de Melo } 1052a8c2190eSArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(inet_diag_register); 1053a8c2190eSArnaldo Carvalho de Melo 1054a8c2190eSArnaldo Carvalho de Melo void inet_diag_unregister(const struct inet_diag_handler *h) 1055a8c2190eSArnaldo Carvalho de Melo { 1056a8c2190eSArnaldo Carvalho de Melo const __u16 type = h->idiag_type; 1057a8c2190eSArnaldo Carvalho de Melo 1058f13c95f0SPavel Emelyanov if (type >= IPPROTO_MAX) 1059a8c2190eSArnaldo Carvalho de Melo return; 1060a8c2190eSArnaldo Carvalho de Melo 1061d523a328SHerbert Xu mutex_lock(&inet_diag_table_mutex); 1062a8c2190eSArnaldo Carvalho de Melo inet_diag_table[type] = NULL; 1063d523a328SHerbert Xu mutex_unlock(&inet_diag_table_mutex); 1064a8c2190eSArnaldo Carvalho de Melo } 1065a8c2190eSArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(inet_diag_unregister); 1066a8c2190eSArnaldo Carvalho de Melo 1067a8c2190eSArnaldo Carvalho de Melo static int __init inet_diag_init(void) 1068a8c2190eSArnaldo Carvalho de Melo { 1069f13c95f0SPavel Emelyanov const int inet_diag_table_size = (IPPROTO_MAX * 1070a8c2190eSArnaldo Carvalho de Melo sizeof(struct inet_diag_handler *)); 1071a8c2190eSArnaldo Carvalho de Melo int err = -ENOMEM; 1072a8c2190eSArnaldo Carvalho de Melo 10730da974f4SPanagiotis Issaris inet_diag_table = kzalloc(inet_diag_table_size, GFP_KERNEL); 1074a8c2190eSArnaldo Carvalho de Melo if (!inet_diag_table) 1075a8c2190eSArnaldo Carvalho de Melo goto out; 1076a8c2190eSArnaldo Carvalho de Melo 1077d366477aSPavel Emelyanov err = sock_diag_register(&inet_diag_handler); 1078d366477aSPavel Emelyanov if (err) 1079d366477aSPavel Emelyanov goto out_free_nl; 1080d366477aSPavel Emelyanov 1081d366477aSPavel Emelyanov err = sock_diag_register(&inet6_diag_handler); 1082d366477aSPavel Emelyanov if (err) 1083d366477aSPavel Emelyanov goto out_free_inet; 1084d366477aSPavel Emelyanov 10858ef874bfSPavel Emelyanov sock_diag_register_inet_compat(inet_diag_rcv_msg_compat); 1086a8c2190eSArnaldo Carvalho de Melo out: 1087a8c2190eSArnaldo Carvalho de Melo return err; 1088d366477aSPavel Emelyanov 1089d366477aSPavel Emelyanov out_free_inet: 1090d366477aSPavel Emelyanov sock_diag_unregister(&inet_diag_handler); 1091d366477aSPavel Emelyanov out_free_nl: 1092a8c2190eSArnaldo Carvalho de Melo kfree(inet_diag_table); 1093a8c2190eSArnaldo Carvalho de Melo goto out; 1094a8c2190eSArnaldo Carvalho de Melo } 1095a8c2190eSArnaldo Carvalho de Melo 1096a8c2190eSArnaldo Carvalho de Melo static void __exit inet_diag_exit(void) 1097a8c2190eSArnaldo Carvalho de Melo { 1098d366477aSPavel Emelyanov sock_diag_unregister(&inet6_diag_handler); 1099d366477aSPavel Emelyanov sock_diag_unregister(&inet_diag_handler); 11008ef874bfSPavel Emelyanov sock_diag_unregister_inet_compat(inet_diag_rcv_msg_compat); 1101a8c2190eSArnaldo Carvalho de Melo kfree(inet_diag_table); 1102a8c2190eSArnaldo Carvalho de Melo } 1103a8c2190eSArnaldo Carvalho de Melo 1104a8c2190eSArnaldo Carvalho de Melo module_init(inet_diag_init); 1105a8c2190eSArnaldo Carvalho de Melo module_exit(inet_diag_exit); 1106a8c2190eSArnaldo Carvalho de Melo MODULE_LICENSE("GPL"); 1107aec8dc62SPavel Emelyanov MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 2 /* AF_INET */); 1108aec8dc62SPavel Emelyanov MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, 10 /* AF_INET6 */); 1109