1*a8c2190eSArnaldo Carvalho de Melo /* 2*a8c2190eSArnaldo Carvalho de Melo * inet_diag.c Module for monitoring INET transport protocols sockets. 3*a8c2190eSArnaldo Carvalho de Melo * 4*a8c2190eSArnaldo Carvalho de Melo * Version: $Id: inet_diag.c,v 1.3 2002/02/01 22:01:04 davem Exp $ 5*a8c2190eSArnaldo Carvalho de Melo * 6*a8c2190eSArnaldo Carvalho de Melo * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 7*a8c2190eSArnaldo Carvalho de Melo * 8*a8c2190eSArnaldo Carvalho de Melo * This program is free software; you can redistribute it and/or 9*a8c2190eSArnaldo Carvalho de Melo * modify it under the terms of the GNU General Public License 10*a8c2190eSArnaldo Carvalho de Melo * as published by the Free Software Foundation; either version 11*a8c2190eSArnaldo Carvalho de Melo * 2 of the License, or (at your option) any later version. 12*a8c2190eSArnaldo Carvalho de Melo */ 13*a8c2190eSArnaldo Carvalho de Melo 14*a8c2190eSArnaldo Carvalho de Melo #include <linux/config.h> 15*a8c2190eSArnaldo Carvalho de Melo #include <linux/module.h> 16*a8c2190eSArnaldo Carvalho de Melo #include <linux/types.h> 17*a8c2190eSArnaldo Carvalho de Melo #include <linux/fcntl.h> 18*a8c2190eSArnaldo Carvalho de Melo #include <linux/random.h> 19*a8c2190eSArnaldo Carvalho de Melo #include <linux/cache.h> 20*a8c2190eSArnaldo Carvalho de Melo #include <linux/init.h> 21*a8c2190eSArnaldo Carvalho de Melo #include <linux/time.h> 22*a8c2190eSArnaldo Carvalho de Melo 23*a8c2190eSArnaldo Carvalho de Melo #include <net/icmp.h> 24*a8c2190eSArnaldo Carvalho de Melo #include <net/tcp.h> 25*a8c2190eSArnaldo Carvalho de Melo #include <net/ipv6.h> 26*a8c2190eSArnaldo Carvalho de Melo #include <net/inet_common.h> 27*a8c2190eSArnaldo Carvalho de Melo #include <net/inet_connection_sock.h> 28*a8c2190eSArnaldo Carvalho de Melo #include <net/inet_hashtables.h> 29*a8c2190eSArnaldo Carvalho de Melo #include <net/inet_timewait_sock.h> 30*a8c2190eSArnaldo Carvalho de Melo #include <net/inet6_hashtables.h> 31*a8c2190eSArnaldo Carvalho de Melo 32*a8c2190eSArnaldo Carvalho de Melo #include <linux/inet.h> 33*a8c2190eSArnaldo Carvalho de Melo #include <linux/stddef.h> 34*a8c2190eSArnaldo Carvalho de Melo 35*a8c2190eSArnaldo Carvalho de Melo #include <linux/inet_diag.h> 36*a8c2190eSArnaldo Carvalho de Melo 37*a8c2190eSArnaldo Carvalho de Melo static const struct inet_diag_handler **inet_diag_table; 38*a8c2190eSArnaldo Carvalho de Melo 39*a8c2190eSArnaldo Carvalho de Melo struct inet_diag_entry { 40*a8c2190eSArnaldo Carvalho de Melo u32 *saddr; 41*a8c2190eSArnaldo Carvalho de Melo u32 *daddr; 42*a8c2190eSArnaldo Carvalho de Melo u16 sport; 43*a8c2190eSArnaldo Carvalho de Melo u16 dport; 44*a8c2190eSArnaldo Carvalho de Melo u16 family; 45*a8c2190eSArnaldo Carvalho de Melo u16 userlocks; 46*a8c2190eSArnaldo Carvalho de Melo }; 47*a8c2190eSArnaldo Carvalho de Melo 48*a8c2190eSArnaldo Carvalho de Melo static struct sock *idiagnl; 49*a8c2190eSArnaldo Carvalho de Melo 50*a8c2190eSArnaldo Carvalho de Melo #define INET_DIAG_PUT(skb, attrtype, attrlen) \ 51*a8c2190eSArnaldo Carvalho de Melo RTA_DATA(__RTA_PUT(skb, attrtype, attrlen)) 52*a8c2190eSArnaldo Carvalho de Melo 53*a8c2190eSArnaldo Carvalho de Melo static int inet_diag_fill(struct sk_buff *skb, struct sock *sk, 54*a8c2190eSArnaldo Carvalho de Melo int ext, u32 pid, u32 seq, u16 nlmsg_flags, 55*a8c2190eSArnaldo Carvalho de Melo const struct nlmsghdr *unlh) 56*a8c2190eSArnaldo Carvalho de Melo { 57*a8c2190eSArnaldo Carvalho de Melo const struct inet_sock *inet = inet_sk(sk); 58*a8c2190eSArnaldo Carvalho de Melo const struct inet_connection_sock *icsk = inet_csk(sk); 59*a8c2190eSArnaldo Carvalho de Melo struct inet_diag_msg *r; 60*a8c2190eSArnaldo Carvalho de Melo struct nlmsghdr *nlh; 61*a8c2190eSArnaldo Carvalho de Melo void *info = NULL; 62*a8c2190eSArnaldo Carvalho de Melo struct inet_diag_meminfo *minfo = NULL; 63*a8c2190eSArnaldo Carvalho de Melo unsigned char *b = skb->tail; 64*a8c2190eSArnaldo Carvalho de Melo const struct inet_diag_handler *handler; 65*a8c2190eSArnaldo Carvalho de Melo 66*a8c2190eSArnaldo Carvalho de Melo handler = inet_diag_table[unlh->nlmsg_type]; 67*a8c2190eSArnaldo Carvalho de Melo BUG_ON(handler == NULL); 68*a8c2190eSArnaldo Carvalho de Melo 69*a8c2190eSArnaldo Carvalho de Melo nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r)); 70*a8c2190eSArnaldo Carvalho de Melo nlh->nlmsg_flags = nlmsg_flags; 71*a8c2190eSArnaldo Carvalho de Melo 72*a8c2190eSArnaldo Carvalho de Melo r = NLMSG_DATA(nlh); 73*a8c2190eSArnaldo Carvalho de Melo if (sk->sk_state != TCP_TIME_WAIT) { 74*a8c2190eSArnaldo Carvalho de Melo if (ext & (1 << (INET_DIAG_MEMINFO - 1))) 75*a8c2190eSArnaldo Carvalho de Melo minfo = INET_DIAG_PUT(skb, INET_DIAG_MEMINFO, 76*a8c2190eSArnaldo Carvalho de Melo sizeof(*minfo)); 77*a8c2190eSArnaldo Carvalho de Melo if (ext & (1 << (INET_DIAG_INFO - 1))) 78*a8c2190eSArnaldo Carvalho de Melo info = INET_DIAG_PUT(skb, INET_DIAG_INFO, 79*a8c2190eSArnaldo Carvalho de Melo handler->idiag_info_size); 80*a8c2190eSArnaldo Carvalho de Melo 81*a8c2190eSArnaldo Carvalho de Melo if ((ext & (1 << (INET_DIAG_CONG - 1))) && icsk->icsk_ca_ops) { 82*a8c2190eSArnaldo Carvalho de Melo size_t len = strlen(icsk->icsk_ca_ops->name); 83*a8c2190eSArnaldo Carvalho de Melo strcpy(INET_DIAG_PUT(skb, INET_DIAG_CONG, len + 1), 84*a8c2190eSArnaldo Carvalho de Melo icsk->icsk_ca_ops->name); 85*a8c2190eSArnaldo Carvalho de Melo } 86*a8c2190eSArnaldo Carvalho de Melo } 87*a8c2190eSArnaldo Carvalho de Melo r->idiag_family = sk->sk_family; 88*a8c2190eSArnaldo Carvalho de Melo r->idiag_state = sk->sk_state; 89*a8c2190eSArnaldo Carvalho de Melo r->idiag_timer = 0; 90*a8c2190eSArnaldo Carvalho de Melo r->idiag_retrans = 0; 91*a8c2190eSArnaldo Carvalho de Melo 92*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_if = sk->sk_bound_dev_if; 93*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_cookie[0] = (u32)(unsigned long)sk; 94*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_cookie[1] = (u32)(((unsigned long)sk >> 31) >> 1); 95*a8c2190eSArnaldo Carvalho de Melo 96*a8c2190eSArnaldo Carvalho de Melo if (r->idiag_state == TCP_TIME_WAIT) { 97*a8c2190eSArnaldo Carvalho de Melo const struct inet_timewait_sock *tw = inet_twsk(sk); 98*a8c2190eSArnaldo Carvalho de Melo long tmo = tw->tw_ttd - jiffies; 99*a8c2190eSArnaldo Carvalho de Melo if (tmo < 0) 100*a8c2190eSArnaldo Carvalho de Melo tmo = 0; 101*a8c2190eSArnaldo Carvalho de Melo 102*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_sport = tw->tw_sport; 103*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_dport = tw->tw_dport; 104*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_src[0] = tw->tw_rcv_saddr; 105*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_dst[0] = tw->tw_daddr; 106*a8c2190eSArnaldo Carvalho de Melo r->idiag_state = tw->tw_substate; 107*a8c2190eSArnaldo Carvalho de Melo r->idiag_timer = 3; 108*a8c2190eSArnaldo Carvalho de Melo r->idiag_expires = (tmo * 1000 + HZ - 1) / HZ; 109*a8c2190eSArnaldo Carvalho de Melo r->idiag_rqueue = 0; 110*a8c2190eSArnaldo Carvalho de Melo r->idiag_wqueue = 0; 111*a8c2190eSArnaldo Carvalho de Melo r->idiag_uid = 0; 112*a8c2190eSArnaldo Carvalho de Melo r->idiag_inode = 0; 113*a8c2190eSArnaldo Carvalho de Melo #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) 114*a8c2190eSArnaldo Carvalho de Melo if (r->idiag_family == AF_INET6) { 115*a8c2190eSArnaldo Carvalho de Melo const struct tcp6_timewait_sock *tcp6tw = tcp6_twsk(sk); 116*a8c2190eSArnaldo Carvalho de Melo 117*a8c2190eSArnaldo Carvalho de Melo ipv6_addr_copy((struct in6_addr *)r->id.idiag_src, 118*a8c2190eSArnaldo Carvalho de Melo &tcp6tw->tw_v6_rcv_saddr); 119*a8c2190eSArnaldo Carvalho de Melo ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst, 120*a8c2190eSArnaldo Carvalho de Melo &tcp6tw->tw_v6_daddr); 121*a8c2190eSArnaldo Carvalho de Melo } 122*a8c2190eSArnaldo Carvalho de Melo #endif 123*a8c2190eSArnaldo Carvalho de Melo nlh->nlmsg_len = skb->tail - b; 124*a8c2190eSArnaldo Carvalho de Melo return skb->len; 125*a8c2190eSArnaldo Carvalho de Melo } 126*a8c2190eSArnaldo Carvalho de Melo 127*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_sport = inet->sport; 128*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_dport = inet->dport; 129*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_src[0] = inet->rcv_saddr; 130*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_dst[0] = inet->daddr; 131*a8c2190eSArnaldo Carvalho de Melo 132*a8c2190eSArnaldo Carvalho de Melo #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) 133*a8c2190eSArnaldo Carvalho de Melo if (r->idiag_family == AF_INET6) { 134*a8c2190eSArnaldo Carvalho de Melo struct ipv6_pinfo *np = inet6_sk(sk); 135*a8c2190eSArnaldo Carvalho de Melo 136*a8c2190eSArnaldo Carvalho de Melo ipv6_addr_copy((struct in6_addr *)r->id.idiag_src, 137*a8c2190eSArnaldo Carvalho de Melo &np->rcv_saddr); 138*a8c2190eSArnaldo Carvalho de Melo ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst, 139*a8c2190eSArnaldo Carvalho de Melo &np->daddr); 140*a8c2190eSArnaldo Carvalho de Melo } 141*a8c2190eSArnaldo Carvalho de Melo #endif 142*a8c2190eSArnaldo Carvalho de Melo 143*a8c2190eSArnaldo Carvalho de Melo #define EXPIRES_IN_MS(tmo) ((tmo - jiffies) * 1000 + HZ - 1) / HZ 144*a8c2190eSArnaldo Carvalho de Melo 145*a8c2190eSArnaldo Carvalho de Melo if (icsk->icsk_pending == ICSK_TIME_RETRANS) { 146*a8c2190eSArnaldo Carvalho de Melo r->idiag_timer = 1; 147*a8c2190eSArnaldo Carvalho de Melo r->idiag_retrans = icsk->icsk_retransmits; 148*a8c2190eSArnaldo Carvalho de Melo r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout); 149*a8c2190eSArnaldo Carvalho de Melo } else if (icsk->icsk_pending == ICSK_TIME_PROBE0) { 150*a8c2190eSArnaldo Carvalho de Melo r->idiag_timer = 4; 151*a8c2190eSArnaldo Carvalho de Melo r->idiag_retrans = icsk->icsk_probes_out; 152*a8c2190eSArnaldo Carvalho de Melo r->idiag_expires = EXPIRES_IN_MS(icsk->icsk_timeout); 153*a8c2190eSArnaldo Carvalho de Melo } else if (timer_pending(&sk->sk_timer)) { 154*a8c2190eSArnaldo Carvalho de Melo r->idiag_timer = 2; 155*a8c2190eSArnaldo Carvalho de Melo r->idiag_retrans = icsk->icsk_probes_out; 156*a8c2190eSArnaldo Carvalho de Melo r->idiag_expires = EXPIRES_IN_MS(sk->sk_timer.expires); 157*a8c2190eSArnaldo Carvalho de Melo } else { 158*a8c2190eSArnaldo Carvalho de Melo r->idiag_timer = 0; 159*a8c2190eSArnaldo Carvalho de Melo r->idiag_expires = 0; 160*a8c2190eSArnaldo Carvalho de Melo } 161*a8c2190eSArnaldo Carvalho de Melo #undef EXPIRES_IN_MS 162*a8c2190eSArnaldo Carvalho de Melo 163*a8c2190eSArnaldo Carvalho de Melo r->idiag_uid = sock_i_uid(sk); 164*a8c2190eSArnaldo Carvalho de Melo r->idiag_inode = sock_i_ino(sk); 165*a8c2190eSArnaldo Carvalho de Melo 166*a8c2190eSArnaldo Carvalho de Melo if (minfo) { 167*a8c2190eSArnaldo Carvalho de Melo minfo->idiag_rmem = atomic_read(&sk->sk_rmem_alloc); 168*a8c2190eSArnaldo Carvalho de Melo minfo->idiag_wmem = sk->sk_wmem_queued; 169*a8c2190eSArnaldo Carvalho de Melo minfo->idiag_fmem = sk->sk_forward_alloc; 170*a8c2190eSArnaldo Carvalho de Melo minfo->idiag_tmem = atomic_read(&sk->sk_wmem_alloc); 171*a8c2190eSArnaldo Carvalho de Melo } 172*a8c2190eSArnaldo Carvalho de Melo 173*a8c2190eSArnaldo Carvalho de Melo handler->idiag_get_info(sk, r, info); 174*a8c2190eSArnaldo Carvalho de Melo 175*a8c2190eSArnaldo Carvalho de Melo if (sk->sk_state < TCP_TIME_WAIT && 176*a8c2190eSArnaldo Carvalho de Melo icsk->icsk_ca_ops && icsk->icsk_ca_ops->get_info) 177*a8c2190eSArnaldo Carvalho de Melo icsk->icsk_ca_ops->get_info(sk, ext, skb); 178*a8c2190eSArnaldo Carvalho de Melo 179*a8c2190eSArnaldo Carvalho de Melo nlh->nlmsg_len = skb->tail - b; 180*a8c2190eSArnaldo Carvalho de Melo return skb->len; 181*a8c2190eSArnaldo Carvalho de Melo 182*a8c2190eSArnaldo Carvalho de Melo rtattr_failure: 183*a8c2190eSArnaldo Carvalho de Melo nlmsg_failure: 184*a8c2190eSArnaldo Carvalho de Melo skb_trim(skb, b - skb->data); 185*a8c2190eSArnaldo Carvalho de Melo return -1; 186*a8c2190eSArnaldo Carvalho de Melo } 187*a8c2190eSArnaldo Carvalho de Melo 188*a8c2190eSArnaldo Carvalho de Melo static int inet_diag_get_exact(struct sk_buff *in_skb, const struct nlmsghdr *nlh) 189*a8c2190eSArnaldo Carvalho de Melo { 190*a8c2190eSArnaldo Carvalho de Melo int err; 191*a8c2190eSArnaldo Carvalho de Melo struct sock *sk; 192*a8c2190eSArnaldo Carvalho de Melo struct inet_diag_req *req = NLMSG_DATA(nlh); 193*a8c2190eSArnaldo Carvalho de Melo struct sk_buff *rep; 194*a8c2190eSArnaldo Carvalho de Melo struct inet_hashinfo *hashinfo; 195*a8c2190eSArnaldo Carvalho de Melo const struct inet_diag_handler *handler; 196*a8c2190eSArnaldo Carvalho de Melo 197*a8c2190eSArnaldo Carvalho de Melo handler = inet_diag_table[nlh->nlmsg_type]; 198*a8c2190eSArnaldo Carvalho de Melo BUG_ON(handler == NULL); 199*a8c2190eSArnaldo Carvalho de Melo hashinfo = handler->idiag_hashinfo; 200*a8c2190eSArnaldo Carvalho de Melo 201*a8c2190eSArnaldo Carvalho de Melo if (req->idiag_family == AF_INET) { 202*a8c2190eSArnaldo Carvalho de Melo sk = inet_lookup(hashinfo, req->id.idiag_dst[0], 203*a8c2190eSArnaldo Carvalho de Melo req->id.idiag_dport, req->id.idiag_src[0], 204*a8c2190eSArnaldo Carvalho de Melo req->id.idiag_sport, req->id.idiag_if); 205*a8c2190eSArnaldo Carvalho de Melo } 206*a8c2190eSArnaldo Carvalho de Melo #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) 207*a8c2190eSArnaldo Carvalho de Melo else if (req->idiag_family == AF_INET6) { 208*a8c2190eSArnaldo Carvalho de Melo sk = inet6_lookup(hashinfo, 209*a8c2190eSArnaldo Carvalho de Melo (struct in6_addr *)req->id.idiag_dst, 210*a8c2190eSArnaldo Carvalho de Melo req->id.idiag_dport, 211*a8c2190eSArnaldo Carvalho de Melo (struct in6_addr *)req->id.idiag_src, 212*a8c2190eSArnaldo Carvalho de Melo req->id.idiag_sport, 213*a8c2190eSArnaldo Carvalho de Melo req->id.idiag_if); 214*a8c2190eSArnaldo Carvalho de Melo } 215*a8c2190eSArnaldo Carvalho de Melo #endif 216*a8c2190eSArnaldo Carvalho de Melo else { 217*a8c2190eSArnaldo Carvalho de Melo return -EINVAL; 218*a8c2190eSArnaldo Carvalho de Melo } 219*a8c2190eSArnaldo Carvalho de Melo 220*a8c2190eSArnaldo Carvalho de Melo if (sk == NULL) 221*a8c2190eSArnaldo Carvalho de Melo return -ENOENT; 222*a8c2190eSArnaldo Carvalho de Melo 223*a8c2190eSArnaldo Carvalho de Melo err = -ESTALE; 224*a8c2190eSArnaldo Carvalho de Melo if ((req->id.idiag_cookie[0] != INET_DIAG_NOCOOKIE || 225*a8c2190eSArnaldo Carvalho de Melo req->id.idiag_cookie[1] != INET_DIAG_NOCOOKIE) && 226*a8c2190eSArnaldo Carvalho de Melo ((u32)(unsigned long)sk != req->id.idiag_cookie[0] || 227*a8c2190eSArnaldo Carvalho de Melo (u32)((((unsigned long)sk) >> 31) >> 1) != req->id.idiag_cookie[1])) 228*a8c2190eSArnaldo Carvalho de Melo goto out; 229*a8c2190eSArnaldo Carvalho de Melo 230*a8c2190eSArnaldo Carvalho de Melo err = -ENOMEM; 231*a8c2190eSArnaldo Carvalho de Melo rep = alloc_skb(NLMSG_SPACE((sizeof(struct inet_diag_msg) + 232*a8c2190eSArnaldo Carvalho de Melo sizeof(struct inet_diag_meminfo) + 233*a8c2190eSArnaldo Carvalho de Melo handler->idiag_info_size + 64)), 234*a8c2190eSArnaldo Carvalho de Melo GFP_KERNEL); 235*a8c2190eSArnaldo Carvalho de Melo if (!rep) 236*a8c2190eSArnaldo Carvalho de Melo goto out; 237*a8c2190eSArnaldo Carvalho de Melo 238*a8c2190eSArnaldo Carvalho de Melo if (inet_diag_fill(rep, sk, req->idiag_ext, 239*a8c2190eSArnaldo Carvalho de Melo NETLINK_CB(in_skb).pid, 240*a8c2190eSArnaldo Carvalho de Melo nlh->nlmsg_seq, 0, nlh) <= 0) 241*a8c2190eSArnaldo Carvalho de Melo BUG(); 242*a8c2190eSArnaldo Carvalho de Melo 243*a8c2190eSArnaldo Carvalho de Melo err = netlink_unicast(idiagnl, rep, NETLINK_CB(in_skb).pid, 244*a8c2190eSArnaldo Carvalho de Melo MSG_DONTWAIT); 245*a8c2190eSArnaldo Carvalho de Melo if (err > 0) 246*a8c2190eSArnaldo Carvalho de Melo err = 0; 247*a8c2190eSArnaldo Carvalho de Melo 248*a8c2190eSArnaldo Carvalho de Melo out: 249*a8c2190eSArnaldo Carvalho de Melo if (sk) { 250*a8c2190eSArnaldo Carvalho de Melo if (sk->sk_state == TCP_TIME_WAIT) 251*a8c2190eSArnaldo Carvalho de Melo inet_twsk_put((struct inet_timewait_sock *)sk); 252*a8c2190eSArnaldo Carvalho de Melo else 253*a8c2190eSArnaldo Carvalho de Melo sock_put(sk); 254*a8c2190eSArnaldo Carvalho de Melo } 255*a8c2190eSArnaldo Carvalho de Melo return err; 256*a8c2190eSArnaldo Carvalho de Melo } 257*a8c2190eSArnaldo Carvalho de Melo 258*a8c2190eSArnaldo Carvalho de Melo static int bitstring_match(const u32 *a1, const u32 *a2, int bits) 259*a8c2190eSArnaldo Carvalho de Melo { 260*a8c2190eSArnaldo Carvalho de Melo int words = bits >> 5; 261*a8c2190eSArnaldo Carvalho de Melo 262*a8c2190eSArnaldo Carvalho de Melo bits &= 0x1f; 263*a8c2190eSArnaldo Carvalho de Melo 264*a8c2190eSArnaldo Carvalho de Melo if (words) { 265*a8c2190eSArnaldo Carvalho de Melo if (memcmp(a1, a2, words << 2)) 266*a8c2190eSArnaldo Carvalho de Melo return 0; 267*a8c2190eSArnaldo Carvalho de Melo } 268*a8c2190eSArnaldo Carvalho de Melo if (bits) { 269*a8c2190eSArnaldo Carvalho de Melo __u32 w1, w2; 270*a8c2190eSArnaldo Carvalho de Melo __u32 mask; 271*a8c2190eSArnaldo Carvalho de Melo 272*a8c2190eSArnaldo Carvalho de Melo w1 = a1[words]; 273*a8c2190eSArnaldo Carvalho de Melo w2 = a2[words]; 274*a8c2190eSArnaldo Carvalho de Melo 275*a8c2190eSArnaldo Carvalho de Melo mask = htonl((0xffffffff) << (32 - bits)); 276*a8c2190eSArnaldo Carvalho de Melo 277*a8c2190eSArnaldo Carvalho de Melo if ((w1 ^ w2) & mask) 278*a8c2190eSArnaldo Carvalho de Melo return 0; 279*a8c2190eSArnaldo Carvalho de Melo } 280*a8c2190eSArnaldo Carvalho de Melo 281*a8c2190eSArnaldo Carvalho de Melo return 1; 282*a8c2190eSArnaldo Carvalho de Melo } 283*a8c2190eSArnaldo Carvalho de Melo 284*a8c2190eSArnaldo Carvalho de Melo 285*a8c2190eSArnaldo Carvalho de Melo static int inet_diag_bc_run(const void *bc, int len, 286*a8c2190eSArnaldo Carvalho de Melo const struct inet_diag_entry *entry) 287*a8c2190eSArnaldo Carvalho de Melo { 288*a8c2190eSArnaldo Carvalho de Melo while (len > 0) { 289*a8c2190eSArnaldo Carvalho de Melo int yes = 1; 290*a8c2190eSArnaldo Carvalho de Melo const struct inet_diag_bc_op *op = bc; 291*a8c2190eSArnaldo Carvalho de Melo 292*a8c2190eSArnaldo Carvalho de Melo switch (op->code) { 293*a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_NOP: 294*a8c2190eSArnaldo Carvalho de Melo break; 295*a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_JMP: 296*a8c2190eSArnaldo Carvalho de Melo yes = 0; 297*a8c2190eSArnaldo Carvalho de Melo break; 298*a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_S_GE: 299*a8c2190eSArnaldo Carvalho de Melo yes = entry->sport >= op[1].no; 300*a8c2190eSArnaldo Carvalho de Melo break; 301*a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_S_LE: 302*a8c2190eSArnaldo Carvalho de Melo yes = entry->dport <= op[1].no; 303*a8c2190eSArnaldo Carvalho de Melo break; 304*a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_D_GE: 305*a8c2190eSArnaldo Carvalho de Melo yes = entry->dport >= op[1].no; 306*a8c2190eSArnaldo Carvalho de Melo break; 307*a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_D_LE: 308*a8c2190eSArnaldo Carvalho de Melo yes = entry->dport <= op[1].no; 309*a8c2190eSArnaldo Carvalho de Melo break; 310*a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_AUTO: 311*a8c2190eSArnaldo Carvalho de Melo yes = !(entry->userlocks & SOCK_BINDPORT_LOCK); 312*a8c2190eSArnaldo Carvalho de Melo break; 313*a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_S_COND: 314*a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_D_COND: { 315*a8c2190eSArnaldo Carvalho de Melo struct inet_diag_hostcond *cond; 316*a8c2190eSArnaldo Carvalho de Melo u32 *addr; 317*a8c2190eSArnaldo Carvalho de Melo 318*a8c2190eSArnaldo Carvalho de Melo cond = (struct inet_diag_hostcond *)(op + 1); 319*a8c2190eSArnaldo Carvalho de Melo if (cond->port != -1 && 320*a8c2190eSArnaldo Carvalho de Melo cond->port != (op->code == INET_DIAG_BC_S_COND ? 321*a8c2190eSArnaldo Carvalho de Melo entry->sport : entry->dport)) { 322*a8c2190eSArnaldo Carvalho de Melo yes = 0; 323*a8c2190eSArnaldo Carvalho de Melo break; 324*a8c2190eSArnaldo Carvalho de Melo } 325*a8c2190eSArnaldo Carvalho de Melo 326*a8c2190eSArnaldo Carvalho de Melo if (cond->prefix_len == 0) 327*a8c2190eSArnaldo Carvalho de Melo break; 328*a8c2190eSArnaldo Carvalho de Melo 329*a8c2190eSArnaldo Carvalho de Melo if (op->code == INET_DIAG_BC_S_COND) 330*a8c2190eSArnaldo Carvalho de Melo addr = entry->saddr; 331*a8c2190eSArnaldo Carvalho de Melo else 332*a8c2190eSArnaldo Carvalho de Melo addr = entry->daddr; 333*a8c2190eSArnaldo Carvalho de Melo 334*a8c2190eSArnaldo Carvalho de Melo if (bitstring_match(addr, cond->addr, cond->prefix_len)) 335*a8c2190eSArnaldo Carvalho de Melo break; 336*a8c2190eSArnaldo Carvalho de Melo if (entry->family == AF_INET6 && 337*a8c2190eSArnaldo Carvalho de Melo cond->family == AF_INET) { 338*a8c2190eSArnaldo Carvalho de Melo if (addr[0] == 0 && addr[1] == 0 && 339*a8c2190eSArnaldo Carvalho de Melo addr[2] == htonl(0xffff) && 340*a8c2190eSArnaldo Carvalho de Melo bitstring_match(addr + 3, cond->addr, 341*a8c2190eSArnaldo Carvalho de Melo cond->prefix_len)) 342*a8c2190eSArnaldo Carvalho de Melo break; 343*a8c2190eSArnaldo Carvalho de Melo } 344*a8c2190eSArnaldo Carvalho de Melo yes = 0; 345*a8c2190eSArnaldo Carvalho de Melo break; 346*a8c2190eSArnaldo Carvalho de Melo } 347*a8c2190eSArnaldo Carvalho de Melo } 348*a8c2190eSArnaldo Carvalho de Melo 349*a8c2190eSArnaldo Carvalho de Melo if (yes) { 350*a8c2190eSArnaldo Carvalho de Melo len -= op->yes; 351*a8c2190eSArnaldo Carvalho de Melo bc += op->yes; 352*a8c2190eSArnaldo Carvalho de Melo } else { 353*a8c2190eSArnaldo Carvalho de Melo len -= op->no; 354*a8c2190eSArnaldo Carvalho de Melo bc += op->no; 355*a8c2190eSArnaldo Carvalho de Melo } 356*a8c2190eSArnaldo Carvalho de Melo } 357*a8c2190eSArnaldo Carvalho de Melo return (len == 0); 358*a8c2190eSArnaldo Carvalho de Melo } 359*a8c2190eSArnaldo Carvalho de Melo 360*a8c2190eSArnaldo Carvalho de Melo static int valid_cc(const void *bc, int len, int cc) 361*a8c2190eSArnaldo Carvalho de Melo { 362*a8c2190eSArnaldo Carvalho de Melo while (len >= 0) { 363*a8c2190eSArnaldo Carvalho de Melo const struct inet_diag_bc_op *op = bc; 364*a8c2190eSArnaldo Carvalho de Melo 365*a8c2190eSArnaldo Carvalho de Melo if (cc > len) 366*a8c2190eSArnaldo Carvalho de Melo return 0; 367*a8c2190eSArnaldo Carvalho de Melo if (cc == len) 368*a8c2190eSArnaldo Carvalho de Melo return 1; 369*a8c2190eSArnaldo Carvalho de Melo if (op->yes < 4) 370*a8c2190eSArnaldo Carvalho de Melo return 0; 371*a8c2190eSArnaldo Carvalho de Melo len -= op->yes; 372*a8c2190eSArnaldo Carvalho de Melo bc += op->yes; 373*a8c2190eSArnaldo Carvalho de Melo } 374*a8c2190eSArnaldo Carvalho de Melo return 0; 375*a8c2190eSArnaldo Carvalho de Melo } 376*a8c2190eSArnaldo Carvalho de Melo 377*a8c2190eSArnaldo Carvalho de Melo static int inet_diag_bc_audit(const void *bytecode, int bytecode_len) 378*a8c2190eSArnaldo Carvalho de Melo { 379*a8c2190eSArnaldo Carvalho de Melo const unsigned char *bc = bytecode; 380*a8c2190eSArnaldo Carvalho de Melo int len = bytecode_len; 381*a8c2190eSArnaldo Carvalho de Melo 382*a8c2190eSArnaldo Carvalho de Melo while (len > 0) { 383*a8c2190eSArnaldo Carvalho de Melo struct inet_diag_bc_op *op = (struct inet_diag_bc_op *)bc; 384*a8c2190eSArnaldo Carvalho de Melo 385*a8c2190eSArnaldo Carvalho de Melo //printk("BC: %d %d %d {%d} / %d\n", op->code, op->yes, op->no, op[1].no, len); 386*a8c2190eSArnaldo Carvalho de Melo switch (op->code) { 387*a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_AUTO: 388*a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_S_COND: 389*a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_D_COND: 390*a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_S_GE: 391*a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_S_LE: 392*a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_D_GE: 393*a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_D_LE: 394*a8c2190eSArnaldo Carvalho de Melo if (op->yes < 4 || op->yes > len + 4) 395*a8c2190eSArnaldo Carvalho de Melo return -EINVAL; 396*a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_JMP: 397*a8c2190eSArnaldo Carvalho de Melo if (op->no < 4 || op->no > len + 4) 398*a8c2190eSArnaldo Carvalho de Melo return -EINVAL; 399*a8c2190eSArnaldo Carvalho de Melo if (op->no < len && 400*a8c2190eSArnaldo Carvalho de Melo !valid_cc(bytecode, bytecode_len, len - op->no)) 401*a8c2190eSArnaldo Carvalho de Melo return -EINVAL; 402*a8c2190eSArnaldo Carvalho de Melo break; 403*a8c2190eSArnaldo Carvalho de Melo case INET_DIAG_BC_NOP: 404*a8c2190eSArnaldo Carvalho de Melo if (op->yes < 4 || op->yes > len + 4) 405*a8c2190eSArnaldo Carvalho de Melo return -EINVAL; 406*a8c2190eSArnaldo Carvalho de Melo break; 407*a8c2190eSArnaldo Carvalho de Melo default: 408*a8c2190eSArnaldo Carvalho de Melo return -EINVAL; 409*a8c2190eSArnaldo Carvalho de Melo } 410*a8c2190eSArnaldo Carvalho de Melo bc += op->yes; 411*a8c2190eSArnaldo Carvalho de Melo len -= op->yes; 412*a8c2190eSArnaldo Carvalho de Melo } 413*a8c2190eSArnaldo Carvalho de Melo return len == 0 ? 0 : -EINVAL; 414*a8c2190eSArnaldo Carvalho de Melo } 415*a8c2190eSArnaldo Carvalho de Melo 416*a8c2190eSArnaldo Carvalho de Melo static int inet_diag_dump_sock(struct sk_buff *skb, struct sock *sk, 417*a8c2190eSArnaldo Carvalho de Melo struct netlink_callback *cb) 418*a8c2190eSArnaldo Carvalho de Melo { 419*a8c2190eSArnaldo Carvalho de Melo struct inet_diag_req *r = NLMSG_DATA(cb->nlh); 420*a8c2190eSArnaldo Carvalho de Melo 421*a8c2190eSArnaldo Carvalho de Melo if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) { 422*a8c2190eSArnaldo Carvalho de Melo struct inet_diag_entry entry; 423*a8c2190eSArnaldo Carvalho de Melo struct rtattr *bc = (struct rtattr *)(r + 1); 424*a8c2190eSArnaldo Carvalho de Melo struct inet_sock *inet = inet_sk(sk); 425*a8c2190eSArnaldo Carvalho de Melo 426*a8c2190eSArnaldo Carvalho de Melo entry.family = sk->sk_family; 427*a8c2190eSArnaldo Carvalho de Melo #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) 428*a8c2190eSArnaldo Carvalho de Melo if (entry.family == AF_INET6) { 429*a8c2190eSArnaldo Carvalho de Melo struct ipv6_pinfo *np = inet6_sk(sk); 430*a8c2190eSArnaldo Carvalho de Melo 431*a8c2190eSArnaldo Carvalho de Melo entry.saddr = np->rcv_saddr.s6_addr32; 432*a8c2190eSArnaldo Carvalho de Melo entry.daddr = np->daddr.s6_addr32; 433*a8c2190eSArnaldo Carvalho de Melo } else 434*a8c2190eSArnaldo Carvalho de Melo #endif 435*a8c2190eSArnaldo Carvalho de Melo { 436*a8c2190eSArnaldo Carvalho de Melo entry.saddr = &inet->rcv_saddr; 437*a8c2190eSArnaldo Carvalho de Melo entry.daddr = &inet->daddr; 438*a8c2190eSArnaldo Carvalho de Melo } 439*a8c2190eSArnaldo Carvalho de Melo entry.sport = inet->num; 440*a8c2190eSArnaldo Carvalho de Melo entry.dport = ntohs(inet->dport); 441*a8c2190eSArnaldo Carvalho de Melo entry.userlocks = sk->sk_userlocks; 442*a8c2190eSArnaldo Carvalho de Melo 443*a8c2190eSArnaldo Carvalho de Melo if (!inet_diag_bc_run(RTA_DATA(bc), RTA_PAYLOAD(bc), &entry)) 444*a8c2190eSArnaldo Carvalho de Melo return 0; 445*a8c2190eSArnaldo Carvalho de Melo } 446*a8c2190eSArnaldo Carvalho de Melo 447*a8c2190eSArnaldo Carvalho de Melo return inet_diag_fill(skb, sk, r->idiag_ext, NETLINK_CB(cb->skb).pid, 448*a8c2190eSArnaldo Carvalho de Melo cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); 449*a8c2190eSArnaldo Carvalho de Melo } 450*a8c2190eSArnaldo Carvalho de Melo 451*a8c2190eSArnaldo Carvalho de Melo static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, 452*a8c2190eSArnaldo Carvalho de Melo struct request_sock *req, 453*a8c2190eSArnaldo Carvalho de Melo u32 pid, u32 seq, 454*a8c2190eSArnaldo Carvalho de Melo const struct nlmsghdr *unlh) 455*a8c2190eSArnaldo Carvalho de Melo { 456*a8c2190eSArnaldo Carvalho de Melo const struct inet_request_sock *ireq = inet_rsk(req); 457*a8c2190eSArnaldo Carvalho de Melo struct inet_sock *inet = inet_sk(sk); 458*a8c2190eSArnaldo Carvalho de Melo unsigned char *b = skb->tail; 459*a8c2190eSArnaldo Carvalho de Melo struct inet_diag_msg *r; 460*a8c2190eSArnaldo Carvalho de Melo struct nlmsghdr *nlh; 461*a8c2190eSArnaldo Carvalho de Melo long tmo; 462*a8c2190eSArnaldo Carvalho de Melo 463*a8c2190eSArnaldo Carvalho de Melo nlh = NLMSG_PUT(skb, pid, seq, unlh->nlmsg_type, sizeof(*r)); 464*a8c2190eSArnaldo Carvalho de Melo nlh->nlmsg_flags = NLM_F_MULTI; 465*a8c2190eSArnaldo Carvalho de Melo r = NLMSG_DATA(nlh); 466*a8c2190eSArnaldo Carvalho de Melo 467*a8c2190eSArnaldo Carvalho de Melo r->idiag_family = sk->sk_family; 468*a8c2190eSArnaldo Carvalho de Melo r->idiag_state = TCP_SYN_RECV; 469*a8c2190eSArnaldo Carvalho de Melo r->idiag_timer = 1; 470*a8c2190eSArnaldo Carvalho de Melo r->idiag_retrans = req->retrans; 471*a8c2190eSArnaldo Carvalho de Melo 472*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_if = sk->sk_bound_dev_if; 473*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_cookie[0] = (u32)(unsigned long)req; 474*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_cookie[1] = (u32)(((unsigned long)req >> 31) >> 1); 475*a8c2190eSArnaldo Carvalho de Melo 476*a8c2190eSArnaldo Carvalho de Melo tmo = req->expires - jiffies; 477*a8c2190eSArnaldo Carvalho de Melo if (tmo < 0) 478*a8c2190eSArnaldo Carvalho de Melo tmo = 0; 479*a8c2190eSArnaldo Carvalho de Melo 480*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_sport = inet->sport; 481*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_dport = ireq->rmt_port; 482*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_src[0] = ireq->loc_addr; 483*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_dst[0] = ireq->rmt_addr; 484*a8c2190eSArnaldo Carvalho de Melo r->idiag_expires = jiffies_to_msecs(tmo); 485*a8c2190eSArnaldo Carvalho de Melo r->idiag_rqueue = 0; 486*a8c2190eSArnaldo Carvalho de Melo r->idiag_wqueue = 0; 487*a8c2190eSArnaldo Carvalho de Melo r->idiag_uid = sock_i_uid(sk); 488*a8c2190eSArnaldo Carvalho de Melo r->idiag_inode = 0; 489*a8c2190eSArnaldo Carvalho de Melo #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) 490*a8c2190eSArnaldo Carvalho de Melo if (r->idiag_family == AF_INET6) { 491*a8c2190eSArnaldo Carvalho de Melo ipv6_addr_copy((struct in6_addr *)r->id.idiag_src, 492*a8c2190eSArnaldo Carvalho de Melo &tcp6_rsk(req)->loc_addr); 493*a8c2190eSArnaldo Carvalho de Melo ipv6_addr_copy((struct in6_addr *)r->id.idiag_dst, 494*a8c2190eSArnaldo Carvalho de Melo &tcp6_rsk(req)->rmt_addr); 495*a8c2190eSArnaldo Carvalho de Melo } 496*a8c2190eSArnaldo Carvalho de Melo #endif 497*a8c2190eSArnaldo Carvalho de Melo nlh->nlmsg_len = skb->tail - b; 498*a8c2190eSArnaldo Carvalho de Melo 499*a8c2190eSArnaldo Carvalho de Melo return skb->len; 500*a8c2190eSArnaldo Carvalho de Melo 501*a8c2190eSArnaldo Carvalho de Melo nlmsg_failure: 502*a8c2190eSArnaldo Carvalho de Melo skb_trim(skb, b - skb->data); 503*a8c2190eSArnaldo Carvalho de Melo return -1; 504*a8c2190eSArnaldo Carvalho de Melo } 505*a8c2190eSArnaldo Carvalho de Melo 506*a8c2190eSArnaldo Carvalho de Melo static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk, 507*a8c2190eSArnaldo Carvalho de Melo struct netlink_callback *cb) 508*a8c2190eSArnaldo Carvalho de Melo { 509*a8c2190eSArnaldo Carvalho de Melo struct inet_diag_entry entry; 510*a8c2190eSArnaldo Carvalho de Melo struct inet_diag_req *r = NLMSG_DATA(cb->nlh); 511*a8c2190eSArnaldo Carvalho de Melo struct inet_connection_sock *icsk = inet_csk(sk); 512*a8c2190eSArnaldo Carvalho de Melo struct listen_sock *lopt; 513*a8c2190eSArnaldo Carvalho de Melo struct rtattr *bc = NULL; 514*a8c2190eSArnaldo Carvalho de Melo struct inet_sock *inet = inet_sk(sk); 515*a8c2190eSArnaldo Carvalho de Melo int j, s_j; 516*a8c2190eSArnaldo Carvalho de Melo int reqnum, s_reqnum; 517*a8c2190eSArnaldo Carvalho de Melo int err = 0; 518*a8c2190eSArnaldo Carvalho de Melo 519*a8c2190eSArnaldo Carvalho de Melo s_j = cb->args[3]; 520*a8c2190eSArnaldo Carvalho de Melo s_reqnum = cb->args[4]; 521*a8c2190eSArnaldo Carvalho de Melo 522*a8c2190eSArnaldo Carvalho de Melo if (s_j > 0) 523*a8c2190eSArnaldo Carvalho de Melo s_j--; 524*a8c2190eSArnaldo Carvalho de Melo 525*a8c2190eSArnaldo Carvalho de Melo entry.family = sk->sk_family; 526*a8c2190eSArnaldo Carvalho de Melo 527*a8c2190eSArnaldo Carvalho de Melo read_lock_bh(&icsk->icsk_accept_queue.syn_wait_lock); 528*a8c2190eSArnaldo Carvalho de Melo 529*a8c2190eSArnaldo Carvalho de Melo lopt = icsk->icsk_accept_queue.listen_opt; 530*a8c2190eSArnaldo Carvalho de Melo if (!lopt || !lopt->qlen) 531*a8c2190eSArnaldo Carvalho de Melo goto out; 532*a8c2190eSArnaldo Carvalho de Melo 533*a8c2190eSArnaldo Carvalho de Melo if (cb->nlh->nlmsg_len > 4 + NLMSG_SPACE(sizeof(*r))) { 534*a8c2190eSArnaldo Carvalho de Melo bc = (struct rtattr *)(r + 1); 535*a8c2190eSArnaldo Carvalho de Melo entry.sport = inet->num; 536*a8c2190eSArnaldo Carvalho de Melo entry.userlocks = sk->sk_userlocks; 537*a8c2190eSArnaldo Carvalho de Melo } 538*a8c2190eSArnaldo Carvalho de Melo 539*a8c2190eSArnaldo Carvalho de Melo for (j = s_j; j < lopt->nr_table_entries; j++) { 540*a8c2190eSArnaldo Carvalho de Melo struct request_sock *req, *head = lopt->syn_table[j]; 541*a8c2190eSArnaldo Carvalho de Melo 542*a8c2190eSArnaldo Carvalho de Melo reqnum = 0; 543*a8c2190eSArnaldo Carvalho de Melo for (req = head; req; reqnum++, req = req->dl_next) { 544*a8c2190eSArnaldo Carvalho de Melo struct inet_request_sock *ireq = inet_rsk(req); 545*a8c2190eSArnaldo Carvalho de Melo 546*a8c2190eSArnaldo Carvalho de Melo if (reqnum < s_reqnum) 547*a8c2190eSArnaldo Carvalho de Melo continue; 548*a8c2190eSArnaldo Carvalho de Melo if (r->id.idiag_dport != ireq->rmt_port && 549*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_dport) 550*a8c2190eSArnaldo Carvalho de Melo continue; 551*a8c2190eSArnaldo Carvalho de Melo 552*a8c2190eSArnaldo Carvalho de Melo if (bc) { 553*a8c2190eSArnaldo Carvalho de Melo entry.saddr = 554*a8c2190eSArnaldo Carvalho de Melo #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) 555*a8c2190eSArnaldo Carvalho de Melo (entry.family == AF_INET6) ? 556*a8c2190eSArnaldo Carvalho de Melo tcp6_rsk(req)->loc_addr.s6_addr32 : 557*a8c2190eSArnaldo Carvalho de Melo #endif 558*a8c2190eSArnaldo Carvalho de Melo &ireq->loc_addr; 559*a8c2190eSArnaldo Carvalho de Melo entry.daddr = 560*a8c2190eSArnaldo Carvalho de Melo #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) 561*a8c2190eSArnaldo Carvalho de Melo (entry.family == AF_INET6) ? 562*a8c2190eSArnaldo Carvalho de Melo tcp6_rsk(req)->rmt_addr.s6_addr32 : 563*a8c2190eSArnaldo Carvalho de Melo #endif 564*a8c2190eSArnaldo Carvalho de Melo &ireq->rmt_addr; 565*a8c2190eSArnaldo Carvalho de Melo entry.dport = ntohs(ireq->rmt_port); 566*a8c2190eSArnaldo Carvalho de Melo 567*a8c2190eSArnaldo Carvalho de Melo if (!inet_diag_bc_run(RTA_DATA(bc), 568*a8c2190eSArnaldo Carvalho de Melo RTA_PAYLOAD(bc), &entry)) 569*a8c2190eSArnaldo Carvalho de Melo continue; 570*a8c2190eSArnaldo Carvalho de Melo } 571*a8c2190eSArnaldo Carvalho de Melo 572*a8c2190eSArnaldo Carvalho de Melo err = inet_diag_fill_req(skb, sk, req, 573*a8c2190eSArnaldo Carvalho de Melo NETLINK_CB(cb->skb).pid, 574*a8c2190eSArnaldo Carvalho de Melo cb->nlh->nlmsg_seq, cb->nlh); 575*a8c2190eSArnaldo Carvalho de Melo if (err < 0) { 576*a8c2190eSArnaldo Carvalho de Melo cb->args[3] = j + 1; 577*a8c2190eSArnaldo Carvalho de Melo cb->args[4] = reqnum; 578*a8c2190eSArnaldo Carvalho de Melo goto out; 579*a8c2190eSArnaldo Carvalho de Melo } 580*a8c2190eSArnaldo Carvalho de Melo } 581*a8c2190eSArnaldo Carvalho de Melo 582*a8c2190eSArnaldo Carvalho de Melo s_reqnum = 0; 583*a8c2190eSArnaldo Carvalho de Melo } 584*a8c2190eSArnaldo Carvalho de Melo 585*a8c2190eSArnaldo Carvalho de Melo out: 586*a8c2190eSArnaldo Carvalho de Melo read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock); 587*a8c2190eSArnaldo Carvalho de Melo 588*a8c2190eSArnaldo Carvalho de Melo return err; 589*a8c2190eSArnaldo Carvalho de Melo } 590*a8c2190eSArnaldo Carvalho de Melo 591*a8c2190eSArnaldo Carvalho de Melo static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) 592*a8c2190eSArnaldo Carvalho de Melo { 593*a8c2190eSArnaldo Carvalho de Melo int i, num; 594*a8c2190eSArnaldo Carvalho de Melo int s_i, s_num; 595*a8c2190eSArnaldo Carvalho de Melo struct inet_diag_req *r = NLMSG_DATA(cb->nlh); 596*a8c2190eSArnaldo Carvalho de Melo const struct inet_diag_handler *handler; 597*a8c2190eSArnaldo Carvalho de Melo struct inet_hashinfo *hashinfo; 598*a8c2190eSArnaldo Carvalho de Melo 599*a8c2190eSArnaldo Carvalho de Melo handler = inet_diag_table[cb->nlh->nlmsg_type]; 600*a8c2190eSArnaldo Carvalho de Melo BUG_ON(handler == NULL); 601*a8c2190eSArnaldo Carvalho de Melo hashinfo = handler->idiag_hashinfo; 602*a8c2190eSArnaldo Carvalho de Melo 603*a8c2190eSArnaldo Carvalho de Melo s_i = cb->args[1]; 604*a8c2190eSArnaldo Carvalho de Melo s_num = num = cb->args[2]; 605*a8c2190eSArnaldo Carvalho de Melo 606*a8c2190eSArnaldo Carvalho de Melo if (cb->args[0] == 0) { 607*a8c2190eSArnaldo Carvalho de Melo if (!(r->idiag_states & (TCPF_LISTEN | TCPF_SYN_RECV))) 608*a8c2190eSArnaldo Carvalho de Melo goto skip_listen_ht; 609*a8c2190eSArnaldo Carvalho de Melo 610*a8c2190eSArnaldo Carvalho de Melo inet_listen_lock(hashinfo); 611*a8c2190eSArnaldo Carvalho de Melo for (i = s_i; i < INET_LHTABLE_SIZE; i++) { 612*a8c2190eSArnaldo Carvalho de Melo struct sock *sk; 613*a8c2190eSArnaldo Carvalho de Melo struct hlist_node *node; 614*a8c2190eSArnaldo Carvalho de Melo 615*a8c2190eSArnaldo Carvalho de Melo num = 0; 616*a8c2190eSArnaldo Carvalho de Melo sk_for_each(sk, node, &hashinfo->listening_hash[i]) { 617*a8c2190eSArnaldo Carvalho de Melo struct inet_sock *inet = inet_sk(sk); 618*a8c2190eSArnaldo Carvalho de Melo 619*a8c2190eSArnaldo Carvalho de Melo if (num < s_num) { 620*a8c2190eSArnaldo Carvalho de Melo num++; 621*a8c2190eSArnaldo Carvalho de Melo continue; 622*a8c2190eSArnaldo Carvalho de Melo } 623*a8c2190eSArnaldo Carvalho de Melo 624*a8c2190eSArnaldo Carvalho de Melo if (r->id.idiag_sport != inet->sport && 625*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_sport) 626*a8c2190eSArnaldo Carvalho de Melo goto next_listen; 627*a8c2190eSArnaldo Carvalho de Melo 628*a8c2190eSArnaldo Carvalho de Melo if (!(r->idiag_states & TCPF_LISTEN) || 629*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_dport || 630*a8c2190eSArnaldo Carvalho de Melo cb->args[3] > 0) 631*a8c2190eSArnaldo Carvalho de Melo goto syn_recv; 632*a8c2190eSArnaldo Carvalho de Melo 633*a8c2190eSArnaldo Carvalho de Melo if (inet_diag_dump_sock(skb, sk, cb) < 0) { 634*a8c2190eSArnaldo Carvalho de Melo inet_listen_unlock(hashinfo); 635*a8c2190eSArnaldo Carvalho de Melo goto done; 636*a8c2190eSArnaldo Carvalho de Melo } 637*a8c2190eSArnaldo Carvalho de Melo 638*a8c2190eSArnaldo Carvalho de Melo syn_recv: 639*a8c2190eSArnaldo Carvalho de Melo if (!(r->idiag_states & TCPF_SYN_RECV)) 640*a8c2190eSArnaldo Carvalho de Melo goto next_listen; 641*a8c2190eSArnaldo Carvalho de Melo 642*a8c2190eSArnaldo Carvalho de Melo if (inet_diag_dump_reqs(skb, sk, cb) < 0) { 643*a8c2190eSArnaldo Carvalho de Melo inet_listen_unlock(hashinfo); 644*a8c2190eSArnaldo Carvalho de Melo goto done; 645*a8c2190eSArnaldo Carvalho de Melo } 646*a8c2190eSArnaldo Carvalho de Melo 647*a8c2190eSArnaldo Carvalho de Melo next_listen: 648*a8c2190eSArnaldo Carvalho de Melo cb->args[3] = 0; 649*a8c2190eSArnaldo Carvalho de Melo cb->args[4] = 0; 650*a8c2190eSArnaldo Carvalho de Melo ++num; 651*a8c2190eSArnaldo Carvalho de Melo } 652*a8c2190eSArnaldo Carvalho de Melo 653*a8c2190eSArnaldo Carvalho de Melo s_num = 0; 654*a8c2190eSArnaldo Carvalho de Melo cb->args[3] = 0; 655*a8c2190eSArnaldo Carvalho de Melo cb->args[4] = 0; 656*a8c2190eSArnaldo Carvalho de Melo } 657*a8c2190eSArnaldo Carvalho de Melo inet_listen_unlock(hashinfo); 658*a8c2190eSArnaldo Carvalho de Melo skip_listen_ht: 659*a8c2190eSArnaldo Carvalho de Melo cb->args[0] = 1; 660*a8c2190eSArnaldo Carvalho de Melo s_i = num = s_num = 0; 661*a8c2190eSArnaldo Carvalho de Melo } 662*a8c2190eSArnaldo Carvalho de Melo 663*a8c2190eSArnaldo Carvalho de Melo if (!(r->idiag_states & ~(TCPF_LISTEN | TCPF_SYN_RECV))) 664*a8c2190eSArnaldo Carvalho de Melo return skb->len; 665*a8c2190eSArnaldo Carvalho de Melo 666*a8c2190eSArnaldo Carvalho de Melo for (i = s_i; i < hashinfo->ehash_size; i++) { 667*a8c2190eSArnaldo Carvalho de Melo struct inet_ehash_bucket *head = &hashinfo->ehash[i]; 668*a8c2190eSArnaldo Carvalho de Melo struct sock *sk; 669*a8c2190eSArnaldo Carvalho de Melo struct hlist_node *node; 670*a8c2190eSArnaldo Carvalho de Melo 671*a8c2190eSArnaldo Carvalho de Melo if (i > s_i) 672*a8c2190eSArnaldo Carvalho de Melo s_num = 0; 673*a8c2190eSArnaldo Carvalho de Melo 674*a8c2190eSArnaldo Carvalho de Melo read_lock_bh(&head->lock); 675*a8c2190eSArnaldo Carvalho de Melo 676*a8c2190eSArnaldo Carvalho de Melo num = 0; 677*a8c2190eSArnaldo Carvalho de Melo sk_for_each(sk, node, &head->chain) { 678*a8c2190eSArnaldo Carvalho de Melo struct inet_sock *inet = inet_sk(sk); 679*a8c2190eSArnaldo Carvalho de Melo 680*a8c2190eSArnaldo Carvalho de Melo if (num < s_num) 681*a8c2190eSArnaldo Carvalho de Melo goto next_normal; 682*a8c2190eSArnaldo Carvalho de Melo if (!(r->idiag_states & (1 << sk->sk_state))) 683*a8c2190eSArnaldo Carvalho de Melo goto next_normal; 684*a8c2190eSArnaldo Carvalho de Melo if (r->id.idiag_sport != inet->sport && 685*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_sport) 686*a8c2190eSArnaldo Carvalho de Melo goto next_normal; 687*a8c2190eSArnaldo Carvalho de Melo if (r->id.idiag_dport != inet->dport && r->id.idiag_dport) 688*a8c2190eSArnaldo Carvalho de Melo goto next_normal; 689*a8c2190eSArnaldo Carvalho de Melo if (inet_diag_dump_sock(skb, sk, cb) < 0) { 690*a8c2190eSArnaldo Carvalho de Melo read_unlock_bh(&head->lock); 691*a8c2190eSArnaldo Carvalho de Melo goto done; 692*a8c2190eSArnaldo Carvalho de Melo } 693*a8c2190eSArnaldo Carvalho de Melo next_normal: 694*a8c2190eSArnaldo Carvalho de Melo ++num; 695*a8c2190eSArnaldo Carvalho de Melo } 696*a8c2190eSArnaldo Carvalho de Melo 697*a8c2190eSArnaldo Carvalho de Melo if (r->idiag_states & TCPF_TIME_WAIT) { 698*a8c2190eSArnaldo Carvalho de Melo sk_for_each(sk, node, 699*a8c2190eSArnaldo Carvalho de Melo &hashinfo->ehash[i + hashinfo->ehash_size].chain) { 700*a8c2190eSArnaldo Carvalho de Melo struct inet_sock *inet = inet_sk(sk); 701*a8c2190eSArnaldo Carvalho de Melo 702*a8c2190eSArnaldo Carvalho de Melo if (num < s_num) 703*a8c2190eSArnaldo Carvalho de Melo goto next_dying; 704*a8c2190eSArnaldo Carvalho de Melo if (r->id.idiag_sport != inet->sport && 705*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_sport) 706*a8c2190eSArnaldo Carvalho de Melo goto next_dying; 707*a8c2190eSArnaldo Carvalho de Melo if (r->id.idiag_dport != inet->dport && 708*a8c2190eSArnaldo Carvalho de Melo r->id.idiag_dport) 709*a8c2190eSArnaldo Carvalho de Melo goto next_dying; 710*a8c2190eSArnaldo Carvalho de Melo if (inet_diag_dump_sock(skb, sk, cb) < 0) { 711*a8c2190eSArnaldo Carvalho de Melo read_unlock_bh(&head->lock); 712*a8c2190eSArnaldo Carvalho de Melo goto done; 713*a8c2190eSArnaldo Carvalho de Melo } 714*a8c2190eSArnaldo Carvalho de Melo next_dying: 715*a8c2190eSArnaldo Carvalho de Melo ++num; 716*a8c2190eSArnaldo Carvalho de Melo } 717*a8c2190eSArnaldo Carvalho de Melo } 718*a8c2190eSArnaldo Carvalho de Melo read_unlock_bh(&head->lock); 719*a8c2190eSArnaldo Carvalho de Melo } 720*a8c2190eSArnaldo Carvalho de Melo 721*a8c2190eSArnaldo Carvalho de Melo done: 722*a8c2190eSArnaldo Carvalho de Melo cb->args[1] = i; 723*a8c2190eSArnaldo Carvalho de Melo cb->args[2] = num; 724*a8c2190eSArnaldo Carvalho de Melo return skb->len; 725*a8c2190eSArnaldo Carvalho de Melo } 726*a8c2190eSArnaldo Carvalho de Melo 727*a8c2190eSArnaldo Carvalho de Melo static int inet_diag_dump_done(struct netlink_callback *cb) 728*a8c2190eSArnaldo Carvalho de Melo { 729*a8c2190eSArnaldo Carvalho de Melo return 0; 730*a8c2190eSArnaldo Carvalho de Melo } 731*a8c2190eSArnaldo Carvalho de Melo 732*a8c2190eSArnaldo Carvalho de Melo 733*a8c2190eSArnaldo Carvalho de Melo static __inline__ int 734*a8c2190eSArnaldo Carvalho de Melo inet_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 735*a8c2190eSArnaldo Carvalho de Melo { 736*a8c2190eSArnaldo Carvalho de Melo if (!(nlh->nlmsg_flags&NLM_F_REQUEST)) 737*a8c2190eSArnaldo Carvalho de Melo return 0; 738*a8c2190eSArnaldo Carvalho de Melo 739*a8c2190eSArnaldo Carvalho de Melo if (nlh->nlmsg_type >= INET_DIAG_GETSOCK_MAX) 740*a8c2190eSArnaldo Carvalho de Melo goto err_inval; 741*a8c2190eSArnaldo Carvalho de Melo 742*a8c2190eSArnaldo Carvalho de Melo if (inet_diag_table[nlh->nlmsg_type] == NULL) 743*a8c2190eSArnaldo Carvalho de Melo return -ENOENT; 744*a8c2190eSArnaldo Carvalho de Melo 745*a8c2190eSArnaldo Carvalho de Melo if (NLMSG_LENGTH(sizeof(struct inet_diag_req)) > skb->len) 746*a8c2190eSArnaldo Carvalho de Melo goto err_inval; 747*a8c2190eSArnaldo Carvalho de Melo 748*a8c2190eSArnaldo Carvalho de Melo if (nlh->nlmsg_flags&NLM_F_DUMP) { 749*a8c2190eSArnaldo Carvalho de Melo if (nlh->nlmsg_len > 750*a8c2190eSArnaldo Carvalho de Melo (4 + NLMSG_SPACE(sizeof(struct inet_diag_req)))) { 751*a8c2190eSArnaldo Carvalho de Melo struct rtattr *rta = (void *)(NLMSG_DATA(nlh) + 752*a8c2190eSArnaldo Carvalho de Melo sizeof(struct inet_diag_req)); 753*a8c2190eSArnaldo Carvalho de Melo if (rta->rta_type != INET_DIAG_REQ_BYTECODE || 754*a8c2190eSArnaldo Carvalho de Melo rta->rta_len < 8 || 755*a8c2190eSArnaldo Carvalho de Melo rta->rta_len > 756*a8c2190eSArnaldo Carvalho de Melo (nlh->nlmsg_len - 757*a8c2190eSArnaldo Carvalho de Melo NLMSG_SPACE(sizeof(struct inet_diag_req)))) 758*a8c2190eSArnaldo Carvalho de Melo goto err_inval; 759*a8c2190eSArnaldo Carvalho de Melo if (inet_diag_bc_audit(RTA_DATA(rta), RTA_PAYLOAD(rta))) 760*a8c2190eSArnaldo Carvalho de Melo goto err_inval; 761*a8c2190eSArnaldo Carvalho de Melo } 762*a8c2190eSArnaldo Carvalho de Melo return netlink_dump_start(idiagnl, skb, nlh, 763*a8c2190eSArnaldo Carvalho de Melo inet_diag_dump, 764*a8c2190eSArnaldo Carvalho de Melo inet_diag_dump_done); 765*a8c2190eSArnaldo Carvalho de Melo } else { 766*a8c2190eSArnaldo Carvalho de Melo return inet_diag_get_exact(skb, nlh); 767*a8c2190eSArnaldo Carvalho de Melo } 768*a8c2190eSArnaldo Carvalho de Melo 769*a8c2190eSArnaldo Carvalho de Melo err_inval: 770*a8c2190eSArnaldo Carvalho de Melo return -EINVAL; 771*a8c2190eSArnaldo Carvalho de Melo } 772*a8c2190eSArnaldo Carvalho de Melo 773*a8c2190eSArnaldo Carvalho de Melo 774*a8c2190eSArnaldo Carvalho de Melo static inline void inet_diag_rcv_skb(struct sk_buff *skb) 775*a8c2190eSArnaldo Carvalho de Melo { 776*a8c2190eSArnaldo Carvalho de Melo int err; 777*a8c2190eSArnaldo Carvalho de Melo struct nlmsghdr * nlh; 778*a8c2190eSArnaldo Carvalho de Melo 779*a8c2190eSArnaldo Carvalho de Melo if (skb->len >= NLMSG_SPACE(0)) { 780*a8c2190eSArnaldo Carvalho de Melo nlh = (struct nlmsghdr *)skb->data; 781*a8c2190eSArnaldo Carvalho de Melo if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len) 782*a8c2190eSArnaldo Carvalho de Melo return; 783*a8c2190eSArnaldo Carvalho de Melo err = inet_diag_rcv_msg(skb, nlh); 784*a8c2190eSArnaldo Carvalho de Melo if (err || nlh->nlmsg_flags & NLM_F_ACK) 785*a8c2190eSArnaldo Carvalho de Melo netlink_ack(skb, nlh, err); 786*a8c2190eSArnaldo Carvalho de Melo } 787*a8c2190eSArnaldo Carvalho de Melo } 788*a8c2190eSArnaldo Carvalho de Melo 789*a8c2190eSArnaldo Carvalho de Melo static void inet_diag_rcv(struct sock *sk, int len) 790*a8c2190eSArnaldo Carvalho de Melo { 791*a8c2190eSArnaldo Carvalho de Melo struct sk_buff *skb; 792*a8c2190eSArnaldo Carvalho de Melo unsigned int qlen = skb_queue_len(&sk->sk_receive_queue); 793*a8c2190eSArnaldo Carvalho de Melo 794*a8c2190eSArnaldo Carvalho de Melo while (qlen-- && (skb = skb_dequeue(&sk->sk_receive_queue))) { 795*a8c2190eSArnaldo Carvalho de Melo inet_diag_rcv_skb(skb); 796*a8c2190eSArnaldo Carvalho de Melo kfree_skb(skb); 797*a8c2190eSArnaldo Carvalho de Melo } 798*a8c2190eSArnaldo Carvalho de Melo } 799*a8c2190eSArnaldo Carvalho de Melo 800*a8c2190eSArnaldo Carvalho de Melo static void tcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, 801*a8c2190eSArnaldo Carvalho de Melo void *_info) 802*a8c2190eSArnaldo Carvalho de Melo { 803*a8c2190eSArnaldo Carvalho de Melo const struct tcp_sock *tp = tcp_sk(sk); 804*a8c2190eSArnaldo Carvalho de Melo struct tcp_info *info = _info; 805*a8c2190eSArnaldo Carvalho de Melo 806*a8c2190eSArnaldo Carvalho de Melo r->idiag_rqueue = tp->rcv_nxt - tp->copied_seq; 807*a8c2190eSArnaldo Carvalho de Melo r->idiag_wqueue = tp->write_seq - tp->snd_una; 808*a8c2190eSArnaldo Carvalho de Melo if (info != NULL) 809*a8c2190eSArnaldo Carvalho de Melo tcp_get_info(sk, info); 810*a8c2190eSArnaldo Carvalho de Melo } 811*a8c2190eSArnaldo Carvalho de Melo 812*a8c2190eSArnaldo Carvalho de Melo static struct inet_diag_handler tcp_diag_handler = { 813*a8c2190eSArnaldo Carvalho de Melo .idiag_hashinfo = &tcp_hashinfo, 814*a8c2190eSArnaldo Carvalho de Melo .idiag_get_info = tcp_diag_get_info, 815*a8c2190eSArnaldo Carvalho de Melo .idiag_type = TCPDIAG_GETSOCK, 816*a8c2190eSArnaldo Carvalho de Melo .idiag_info_size = sizeof(struct tcp_info), 817*a8c2190eSArnaldo Carvalho de Melo }; 818*a8c2190eSArnaldo Carvalho de Melo 819*a8c2190eSArnaldo Carvalho de Melo static DEFINE_SPINLOCK(inet_diag_register_lock); 820*a8c2190eSArnaldo Carvalho de Melo 821*a8c2190eSArnaldo Carvalho de Melo int inet_diag_register(const struct inet_diag_handler *h) 822*a8c2190eSArnaldo Carvalho de Melo { 823*a8c2190eSArnaldo Carvalho de Melo const __u16 type = h->idiag_type; 824*a8c2190eSArnaldo Carvalho de Melo int err = -EINVAL; 825*a8c2190eSArnaldo Carvalho de Melo 826*a8c2190eSArnaldo Carvalho de Melo if (type >= INET_DIAG_GETSOCK_MAX) 827*a8c2190eSArnaldo Carvalho de Melo goto out; 828*a8c2190eSArnaldo Carvalho de Melo 829*a8c2190eSArnaldo Carvalho de Melo spin_lock(&inet_diag_register_lock); 830*a8c2190eSArnaldo Carvalho de Melo err = -EEXIST; 831*a8c2190eSArnaldo Carvalho de Melo if (inet_diag_table[type] == NULL) { 832*a8c2190eSArnaldo Carvalho de Melo inet_diag_table[type] = h; 833*a8c2190eSArnaldo Carvalho de Melo err = 0; 834*a8c2190eSArnaldo Carvalho de Melo } 835*a8c2190eSArnaldo Carvalho de Melo spin_unlock(&inet_diag_register_lock); 836*a8c2190eSArnaldo Carvalho de Melo out: 837*a8c2190eSArnaldo Carvalho de Melo return err; 838*a8c2190eSArnaldo Carvalho de Melo } 839*a8c2190eSArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(inet_diag_register); 840*a8c2190eSArnaldo Carvalho de Melo 841*a8c2190eSArnaldo Carvalho de Melo void inet_diag_unregister(const struct inet_diag_handler *h) 842*a8c2190eSArnaldo Carvalho de Melo { 843*a8c2190eSArnaldo Carvalho de Melo const __u16 type = h->idiag_type; 844*a8c2190eSArnaldo Carvalho de Melo 845*a8c2190eSArnaldo Carvalho de Melo if (type >= INET_DIAG_GETSOCK_MAX) 846*a8c2190eSArnaldo Carvalho de Melo return; 847*a8c2190eSArnaldo Carvalho de Melo 848*a8c2190eSArnaldo Carvalho de Melo spin_lock(&inet_diag_register_lock); 849*a8c2190eSArnaldo Carvalho de Melo inet_diag_table[type] = NULL; 850*a8c2190eSArnaldo Carvalho de Melo spin_unlock(&inet_diag_register_lock); 851*a8c2190eSArnaldo Carvalho de Melo 852*a8c2190eSArnaldo Carvalho de Melo synchronize_rcu(); 853*a8c2190eSArnaldo Carvalho de Melo } 854*a8c2190eSArnaldo Carvalho de Melo EXPORT_SYMBOL_GPL(inet_diag_unregister); 855*a8c2190eSArnaldo Carvalho de Melo 856*a8c2190eSArnaldo Carvalho de Melo static int __init inet_diag_init(void) 857*a8c2190eSArnaldo Carvalho de Melo { 858*a8c2190eSArnaldo Carvalho de Melo const int inet_diag_table_size = (INET_DIAG_GETSOCK_MAX * 859*a8c2190eSArnaldo Carvalho de Melo sizeof(struct inet_diag_handler *)); 860*a8c2190eSArnaldo Carvalho de Melo int err = -ENOMEM; 861*a8c2190eSArnaldo Carvalho de Melo 862*a8c2190eSArnaldo Carvalho de Melo inet_diag_table = kmalloc(inet_diag_table_size, GFP_KERNEL); 863*a8c2190eSArnaldo Carvalho de Melo if (!inet_diag_table) 864*a8c2190eSArnaldo Carvalho de Melo goto out; 865*a8c2190eSArnaldo Carvalho de Melo 866*a8c2190eSArnaldo Carvalho de Melo memset(inet_diag_table, 0, inet_diag_table_size); 867*a8c2190eSArnaldo Carvalho de Melo 868*a8c2190eSArnaldo Carvalho de Melo idiagnl = netlink_kernel_create(NETLINK_INET_DIAG, inet_diag_rcv, 869*a8c2190eSArnaldo Carvalho de Melo THIS_MODULE); 870*a8c2190eSArnaldo Carvalho de Melo if (idiagnl == NULL) 871*a8c2190eSArnaldo Carvalho de Melo goto out_free_table; 872*a8c2190eSArnaldo Carvalho de Melo 873*a8c2190eSArnaldo Carvalho de Melo err = inet_diag_register(&tcp_diag_handler); 874*a8c2190eSArnaldo Carvalho de Melo if (err) 875*a8c2190eSArnaldo Carvalho de Melo goto out_sock_release; 876*a8c2190eSArnaldo Carvalho de Melo out: 877*a8c2190eSArnaldo Carvalho de Melo return err; 878*a8c2190eSArnaldo Carvalho de Melo out_sock_release: 879*a8c2190eSArnaldo Carvalho de Melo sock_release(idiagnl->sk_socket); 880*a8c2190eSArnaldo Carvalho de Melo out_free_table: 881*a8c2190eSArnaldo Carvalho de Melo kfree(inet_diag_table); 882*a8c2190eSArnaldo Carvalho de Melo goto out; 883*a8c2190eSArnaldo Carvalho de Melo } 884*a8c2190eSArnaldo Carvalho de Melo 885*a8c2190eSArnaldo Carvalho de Melo static void __exit inet_diag_exit(void) 886*a8c2190eSArnaldo Carvalho de Melo { 887*a8c2190eSArnaldo Carvalho de Melo sock_release(idiagnl->sk_socket); 888*a8c2190eSArnaldo Carvalho de Melo kfree(inet_diag_table); 889*a8c2190eSArnaldo Carvalho de Melo } 890*a8c2190eSArnaldo Carvalho de Melo 891*a8c2190eSArnaldo Carvalho de Melo module_init(inet_diag_init); 892*a8c2190eSArnaldo Carvalho de Melo module_exit(inet_diag_exit); 893*a8c2190eSArnaldo Carvalho de Melo MODULE_LICENSE("GPL"); 894