2005-04-17 02:20:36 +04:00
/*
2005-08-12 19:51:49 +04:00
* inet_diag . c Module for monitoring INET transport protocols sockets .
2005-04-17 02:20:36 +04:00
*
2005-08-12 19:51:49 +04:00
* Version : $ Id : inet_diag . c , v 1.3 2002 / 02 / 01 22 : 01 : 04 davem Exp $
2005-04-17 02:20:36 +04:00
*
* Authors : Alexey Kuznetsov , < kuznet @ ms2 . inr . ac . ru >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <linux/config.h>
# include <linux/module.h>
# include <linux/types.h>
# include <linux/fcntl.h>
# include <linux/random.h>
# include <linux/cache.h>
# include <linux/init.h>
# include <linux/time.h>
# include <net/icmp.h>
# include <net/tcp.h>
# include <net/ipv6.h>
# include <net/inet_common.h>
2005-08-12 16:19:38 +04:00
# include <net/inet_connection_sock.h>
# include <net/inet_hashtables.h>
# include <net/inet_timewait_sock.h>
# include <net/inet6_hashtables.h>
2005-04-17 02:20:36 +04:00
# include <linux/inet.h>
# include <linux/stddef.h>
2005-08-12 19:56:38 +04:00
# include <linux/inet_diag.h>
2005-04-17 02:20:36 +04:00
2005-08-12 16:27:49 +04:00
static const struct inet_diag_handler * * inet_diag_table ;
2005-08-12 19:51:49 +04:00
struct inet_diag_entry {
2005-04-17 02:20:36 +04:00
u32 * saddr ;
u32 * daddr ;
u16 sport ;
u16 dport ;
u16 family ;
u16 userlocks ;
} ;
2005-08-12 19:51:49 +04:00
static struct sock * idiagnl ;
2005-04-17 02:20:36 +04:00
2005-08-12 19:51:49 +04:00
# define INET_DIAG_PUT(skb, attrtype, attrlen) \
2005-06-23 23:20:36 +04:00
RTA_DATA ( __RTA_PUT ( skb , attrtype , attrlen ) )
2005-04-17 02:20:36 +04:00
2005-08-12 19:51:49 +04:00
static int inet_diag_fill ( struct sk_buff * skb , struct sock * sk ,
2005-08-10 12:54:28 +04:00
int ext , u32 pid , u32 seq , u16 nlmsg_flags ,
const struct nlmsghdr * unlh )
2005-04-17 02:20:36 +04:00
{
2005-08-10 07:10:42 +04:00
const struct inet_sock * inet = inet_sk ( sk ) ;
const struct inet_connection_sock * icsk = inet_csk ( sk ) ;
2005-08-12 19:51:49 +04:00
struct inet_diag_msg * r ;
2005-04-17 02:20:36 +04:00
struct nlmsghdr * nlh ;
2005-08-12 16:27:49 +04:00
void * info = NULL ;
2005-08-12 19:51:49 +04:00
struct inet_diag_meminfo * minfo = NULL ;
2005-04-17 02:20:36 +04:00
unsigned char * b = skb - > tail ;
2005-08-12 16:27:49 +04:00
const struct inet_diag_handler * handler ;
handler = inet_diag_table [ unlh - > nlmsg_type ] ;
BUG_ON ( handler = = NULL ) ;
2005-04-17 02:20:36 +04:00
2005-08-10 12:54:28 +04:00
nlh = NLMSG_PUT ( skb , pid , seq , unlh - > nlmsg_type , sizeof ( * r ) ) ;
2005-04-17 02:20:36 +04:00
nlh - > nlmsg_flags = nlmsg_flags ;
2005-08-12 16:27:49 +04:00
2005-04-17 02:20:36 +04:00
r = NLMSG_DATA ( nlh ) ;
if ( sk - > sk_state ! = TCP_TIME_WAIT ) {
2005-08-12 19:51:49 +04:00
if ( ext & ( 1 < < ( INET_DIAG_MEMINFO - 1 ) ) )
minfo = INET_DIAG_PUT ( skb , INET_DIAG_MEMINFO ,
sizeof ( * minfo ) ) ;
if ( ext & ( 1 < < ( INET_DIAG_INFO - 1 ) ) )
info = INET_DIAG_PUT ( skb , INET_DIAG_INFO ,
2005-08-12 16:27:49 +04:00
handler - > idiag_info_size ) ;
2005-04-17 02:20:36 +04:00
2005-08-12 19:51:49 +04:00
if ( ( ext & ( 1 < < ( INET_DIAG_CONG - 1 ) ) ) & & icsk - > icsk_ca_ops ) {
2005-08-10 11:03:31 +04:00
size_t len = strlen ( icsk - > icsk_ca_ops - > name ) ;
2005-08-12 19:51:49 +04:00
strcpy ( INET_DIAG_PUT ( skb , INET_DIAG_CONG , len + 1 ) ,
2005-08-10 11:03:31 +04:00
icsk - > icsk_ca_ops - > name ) ;
2005-06-23 23:21:28 +04:00
}
2005-04-17 02:20:36 +04:00
}
2005-08-12 19:51:49 +04:00
r - > idiag_family = sk - > sk_family ;
r - > idiag_state = sk - > sk_state ;
r - > idiag_timer = 0 ;
r - > idiag_retrans = 0 ;
2005-04-17 02:20:36 +04:00
2005-08-12 19:51:49 +04:00
r - > id . idiag_if = sk - > sk_bound_dev_if ;
r - > id . idiag_cookie [ 0 ] = ( u32 ) ( unsigned long ) sk ;
r - > id . idiag_cookie [ 1 ] = ( u32 ) ( ( ( unsigned long ) sk > > 31 ) > > 1 ) ;
2005-04-17 02:20:36 +04:00
2005-08-12 19:51:49 +04:00
if ( r - > idiag_state = = TCP_TIME_WAIT ) {
2005-08-10 07:09:30 +04:00
const struct inet_timewait_sock * tw = inet_twsk ( sk ) ;
2005-04-17 02:20:36 +04:00
long tmo = tw - > tw_ttd - jiffies ;
if ( tmo < 0 )
tmo = 0 ;
2005-08-12 19:51:49 +04:00
r - > id . idiag_sport = tw - > tw_sport ;
r - > id . idiag_dport = tw - > tw_dport ;
r - > id . idiag_src [ 0 ] = tw - > tw_rcv_saddr ;
r - > id . idiag_dst [ 0 ] = tw - > tw_daddr ;
r - > idiag_state = tw - > tw_substate ;
r - > idiag_timer = 3 ;
r - > idiag_expires = ( tmo * 1000 + HZ - 1 ) / HZ ;
r - > idiag_rqueue = 0 ;
r - > idiag_wqueue = 0 ;
r - > idiag_uid = 0 ;
r - > idiag_inode = 0 ;
2005-08-12 16:19:38 +04:00
# if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
2005-08-12 19:51:49 +04:00
if ( r - > idiag_family = = AF_INET6 ) {
2005-08-10 07:09:30 +04:00
const struct tcp6_timewait_sock * tcp6tw = tcp6_twsk ( sk ) ;
2005-08-12 19:51:49 +04:00
ipv6_addr_copy ( ( struct in6_addr * ) r - > id . idiag_src ,
2005-08-10 07:09:30 +04:00
& tcp6tw - > tw_v6_rcv_saddr ) ;
2005-08-12 19:51:49 +04:00
ipv6_addr_copy ( ( struct in6_addr * ) r - > id . idiag_dst ,
2005-08-10 07:09:30 +04:00
& tcp6tw - > tw_v6_daddr ) ;
2005-04-17 02:20:36 +04:00
}
# endif
nlh - > nlmsg_len = skb - > tail - b ;
return skb - > len ;
}
2005-08-12 19:51:49 +04:00
r - > id . idiag_sport = inet - > sport ;
r - > id . idiag_dport = inet - > dport ;
r - > id . idiag_src [ 0 ] = inet - > rcv_saddr ;
r - > id . idiag_dst [ 0 ] = inet - > daddr ;
2005-04-17 02:20:36 +04:00
2005-08-12 16:19:38 +04:00
# if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
2005-08-12 19:51:49 +04:00
if ( r - > idiag_family = = AF_INET6 ) {
2005-04-17 02:20:36 +04:00
struct ipv6_pinfo * np = inet6_sk ( sk ) ;
2005-08-12 19:51:49 +04:00
ipv6_addr_copy ( ( struct in6_addr * ) r - > id . idiag_src ,
2005-04-17 02:20:36 +04:00
& np - > rcv_saddr ) ;
2005-08-12 19:51:49 +04:00
ipv6_addr_copy ( ( struct in6_addr * ) r - > id . idiag_dst ,
2005-04-17 02:20:36 +04:00
& np - > daddr ) ;
}
# endif
2005-08-12 19:56:38 +04:00
# define EXPIRES_IN_MS(tmo) ((tmo - jiffies) * 1000 + HZ - 1) / HZ
2005-04-17 02:20:36 +04:00
2005-08-10 07:10:42 +04:00
if ( icsk - > icsk_pending = = ICSK_TIME_RETRANS ) {
2005-08-12 19:51:49 +04:00
r - > idiag_timer = 1 ;
r - > idiag_retrans = icsk - > icsk_retransmits ;
r - > idiag_expires = EXPIRES_IN_MS ( icsk - > icsk_timeout ) ;
2005-08-10 07:10:42 +04:00
} else if ( icsk - > icsk_pending = = ICSK_TIME_PROBE0 ) {
2005-08-12 19:51:49 +04:00
r - > idiag_timer = 4 ;
r - > idiag_retrans = icsk - > icsk_probes_out ;
r - > idiag_expires = EXPIRES_IN_MS ( icsk - > icsk_timeout ) ;
2005-04-17 02:20:36 +04:00
} else if ( timer_pending ( & sk - > sk_timer ) ) {
2005-08-12 19:51:49 +04:00
r - > idiag_timer = 2 ;
r - > idiag_retrans = icsk - > icsk_probes_out ;
r - > idiag_expires = EXPIRES_IN_MS ( sk - > sk_timer . expires ) ;
2005-04-17 02:20:36 +04:00
} else {
2005-08-12 19:51:49 +04:00
r - > idiag_timer = 0 ;
r - > idiag_expires = 0 ;
2005-04-17 02:20:36 +04:00
}
# undef EXPIRES_IN_MS
2005-08-10 12:54:28 +04:00
2005-08-12 19:51:49 +04:00
r - > idiag_uid = sock_i_uid ( sk ) ;
r - > idiag_inode = sock_i_ino ( sk ) ;
2005-04-17 02:20:36 +04:00
if ( minfo ) {
2005-08-12 19:51:49 +04:00
minfo - > idiag_rmem = atomic_read ( & sk - > sk_rmem_alloc ) ;
minfo - > idiag_wmem = sk - > sk_wmem_queued ;
minfo - > idiag_fmem = sk - > sk_forward_alloc ;
minfo - > idiag_tmem = atomic_read ( & sk - > sk_wmem_alloc ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-12 16:27:49 +04:00
handler - > idiag_get_info ( sk , r , info ) ;
2005-04-17 02:20:36 +04:00
2005-08-10 11:03:31 +04:00
if ( sk - > sk_state < TCP_TIME_WAIT & &
icsk - > icsk_ca_ops & & icsk - > icsk_ca_ops - > get_info )
icsk - > icsk_ca_ops - > get_info ( sk , ext , skb ) ;
2005-04-17 02:20:36 +04:00
nlh - > nlmsg_len = skb - > tail - b ;
return skb - > len ;
2005-06-23 23:20:36 +04:00
rtattr_failure :
2005-04-17 02:20:36 +04:00
nlmsg_failure :
skb_trim ( skb , b - skb - > data ) ;
return - 1 ;
}
2005-08-12 19:51:49 +04:00
static int inet_diag_get_exact ( struct sk_buff * in_skb , const struct nlmsghdr * nlh )
2005-04-17 02:20:36 +04:00
{
int err ;
struct sock * sk ;
2005-08-12 19:51:49 +04:00
struct inet_diag_req * req = NLMSG_DATA ( nlh ) ;
2005-04-17 02:20:36 +04:00
struct sk_buff * rep ;
2005-08-12 16:27:49 +04:00
struct inet_hashinfo * hashinfo ;
const struct inet_diag_handler * handler ;
handler = inet_diag_table [ nlh - > nlmsg_type ] ;
BUG_ON ( handler = = NULL ) ;
hashinfo = handler - > idiag_hashinfo ;
2005-08-12 19:51:49 +04:00
if ( req - > idiag_family = = AF_INET ) {
sk = inet_lookup ( hashinfo , req - > id . idiag_dst [ 0 ] ,
req - > id . idiag_dport , req - > id . idiag_src [ 0 ] ,
req - > id . idiag_sport , req - > id . idiag_if ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-12 16:19:38 +04:00
# if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
2005-08-12 19:51:49 +04:00
else if ( req - > idiag_family = = AF_INET6 ) {
2005-08-12 16:19:38 +04:00
sk = inet6_lookup ( hashinfo ,
2005-08-12 19:51:49 +04:00
( struct in6_addr * ) req - > id . idiag_dst ,
req - > id . idiag_dport ,
( struct in6_addr * ) req - > id . idiag_src ,
req - > id . idiag_sport ,
req - > id . idiag_if ) ;
2005-04-17 02:20:36 +04:00
}
# endif
else {
return - EINVAL ;
}
if ( sk = = NULL )
return - ENOENT ;
err = - ESTALE ;
2005-08-12 19:51:49 +04:00
if ( ( req - > id . idiag_cookie [ 0 ] ! = INET_DIAG_NOCOOKIE | |
req - > id . idiag_cookie [ 1 ] ! = INET_DIAG_NOCOOKIE ) & &
( ( u32 ) ( unsigned long ) sk ! = req - > id . idiag_cookie [ 0 ] | |
( u32 ) ( ( ( ( unsigned long ) sk ) > > 31 ) > > 1 ) ! = req - > id . idiag_cookie [ 1 ] ) )
2005-04-17 02:20:36 +04:00
goto out ;
err = - ENOMEM ;
2005-08-12 19:51:49 +04:00
rep = alloc_skb ( NLMSG_SPACE ( ( sizeof ( struct inet_diag_msg ) +
sizeof ( struct inet_diag_meminfo ) +
2005-08-12 16:27:49 +04:00
handler - > idiag_info_size + 64 ) ) ,
GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! rep )
goto out ;
2005-08-12 19:51:49 +04:00
if ( inet_diag_fill ( rep , sk , req - > idiag_ext ,
2005-04-17 02:20:36 +04:00
NETLINK_CB ( in_skb ) . pid ,
2005-08-10 12:54:28 +04:00
nlh - > nlmsg_seq , 0 , nlh ) < = 0 )
2005-04-17 02:20:36 +04:00
BUG ( ) ;
2005-08-12 19:51:49 +04:00
err = netlink_unicast ( idiagnl , rep , NETLINK_CB ( in_skb ) . pid ,
MSG_DONTWAIT ) ;
2005-04-17 02:20:36 +04:00
if ( err > 0 )
err = 0 ;
out :
if ( sk ) {
if ( sk - > sk_state = = TCP_TIME_WAIT )
2005-08-10 07:09:30 +04:00
inet_twsk_put ( ( struct inet_timewait_sock * ) sk ) ;
2005-04-17 02:20:36 +04:00
else
sock_put ( sk ) ;
}
return err ;
}
static int bitstring_match ( const u32 * a1 , const u32 * a2 , int bits )
{
int words = bits > > 5 ;
bits & = 0x1f ;
if ( words ) {
if ( memcmp ( a1 , a2 , words < < 2 ) )
return 0 ;
}
if ( bits ) {
__u32 w1 , w2 ;
__u32 mask ;
w1 = a1 [ words ] ;
w2 = a2 [ words ] ;
mask = htonl ( ( 0xffffffff ) < < ( 32 - bits ) ) ;
if ( ( w1 ^ w2 ) & mask )
return 0 ;
}
return 1 ;
}
2005-08-12 19:51:49 +04:00
static int inet_diag_bc_run ( const void * bc , int len ,
const struct inet_diag_entry * entry )
2005-04-17 02:20:36 +04:00
{
while ( len > 0 ) {
int yes = 1 ;
2005-08-12 19:51:49 +04:00
const struct inet_diag_bc_op * op = bc ;
2005-04-17 02:20:36 +04:00
switch ( op - > code ) {
2005-08-12 19:51:49 +04:00
case INET_DIAG_BC_NOP :
2005-04-17 02:20:36 +04:00
break ;
2005-08-12 19:51:49 +04:00
case INET_DIAG_BC_JMP :
2005-04-17 02:20:36 +04:00
yes = 0 ;
break ;
2005-08-12 19:51:49 +04:00
case INET_DIAG_BC_S_GE :
2005-04-17 02:20:36 +04:00
yes = entry - > sport > = op [ 1 ] . no ;
break ;
2005-08-12 19:51:49 +04:00
case INET_DIAG_BC_S_LE :
2005-04-17 02:20:36 +04:00
yes = entry - > dport < = op [ 1 ] . no ;
break ;
2005-08-12 19:51:49 +04:00
case INET_DIAG_BC_D_GE :
2005-04-17 02:20:36 +04:00
yes = entry - > dport > = op [ 1 ] . no ;
break ;
2005-08-12 19:51:49 +04:00
case INET_DIAG_BC_D_LE :
2005-04-17 02:20:36 +04:00
yes = entry - > dport < = op [ 1 ] . no ;
break ;
2005-08-12 19:51:49 +04:00
case INET_DIAG_BC_AUTO :
2005-04-17 02:20:36 +04:00
yes = ! ( entry - > userlocks & SOCK_BINDPORT_LOCK ) ;
break ;
2005-08-12 19:51:49 +04:00
case INET_DIAG_BC_S_COND :
2005-08-12 19:56:38 +04:00
case INET_DIAG_BC_D_COND : {
struct inet_diag_hostcond * cond ;
2005-04-17 02:20:36 +04:00
u32 * addr ;
2005-08-12 19:56:38 +04:00
cond = ( struct inet_diag_hostcond * ) ( op + 1 ) ;
2005-04-17 02:20:36 +04:00
if ( cond - > port ! = - 1 & &
2005-08-12 19:51:49 +04:00
cond - > port ! = ( op - > code = = INET_DIAG_BC_S_COND ?
2005-04-17 02:20:36 +04:00
entry - > sport : entry - > dport ) ) {
yes = 0 ;
break ;
}
if ( cond - > prefix_len = = 0 )
break ;
2005-08-12 19:51:49 +04:00
if ( op - > code = = INET_DIAG_BC_S_COND )
2005-04-17 02:20:36 +04:00
addr = entry - > saddr ;
else
addr = entry - > daddr ;
if ( bitstring_match ( addr , cond - > addr , cond - > prefix_len ) )
break ;
if ( entry - > family = = AF_INET6 & &
cond - > family = = AF_INET ) {
if ( addr [ 0 ] = = 0 & & addr [ 1 ] = = 0 & &
addr [ 2 ] = = htonl ( 0xffff ) & &
2005-08-12 19:56:38 +04:00
bitstring_match ( addr + 3 , cond - > addr ,
cond - > prefix_len ) )
2005-04-17 02:20:36 +04:00
break ;
}
yes = 0 ;
break ;
}
}
if ( yes ) {
len - = op - > yes ;
bc + = op - > yes ;
} else {
len - = op - > no ;
bc + = op - > no ;
}
}
return ( len = = 0 ) ;
}
static int valid_cc ( const void * bc , int len , int cc )
{
while ( len > = 0 ) {
2005-08-12 19:51:49 +04:00
const struct inet_diag_bc_op * op = bc ;
2005-04-17 02:20:36 +04:00
if ( cc > len )
return 0 ;
if ( cc = = len )
return 1 ;
if ( op - > yes < 4 )
return 0 ;
len - = op - > yes ;
bc + = op - > yes ;
}
return 0 ;
}
2005-08-12 19:51:49 +04:00
static int inet_diag_bc_audit ( const void * bytecode , int bytecode_len )
2005-04-17 02:20:36 +04:00
{
const unsigned char * bc = bytecode ;
int len = bytecode_len ;
while ( len > 0 ) {
2005-08-12 19:56:38 +04:00
struct inet_diag_bc_op * op = ( struct inet_diag_bc_op * ) bc ;
2005-04-17 02:20:36 +04:00
//printk("BC: %d %d %d {%d} / %d\n", op->code, op->yes, op->no, op[1].no, len);
switch ( op - > code ) {
2005-08-12 19:51:49 +04:00
case INET_DIAG_BC_AUTO :
case INET_DIAG_BC_S_COND :
case INET_DIAG_BC_D_COND :
case INET_DIAG_BC_S_GE :
case INET_DIAG_BC_S_LE :
case INET_DIAG_BC_D_GE :
case INET_DIAG_BC_D_LE :
2005-08-12 19:56:38 +04:00
if ( op - > yes < 4 | | op - > yes > len + 4 )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2005-08-12 19:51:49 +04:00
case INET_DIAG_BC_JMP :
2005-08-12 19:56:38 +04:00
if ( op - > no < 4 | | op - > no > len + 4 )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
if ( op - > no < len & &
2005-08-12 19:56:38 +04:00
! valid_cc ( bytecode , bytecode_len , len - op - > no ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
break ;
2005-08-12 19:51:49 +04:00
case INET_DIAG_BC_NOP :
2005-08-12 19:56:38 +04:00
if ( op - > yes < 4 | | op - > yes > len + 4 )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
break ;
default :
return - EINVAL ;
}
bc + = op - > yes ;
len - = op - > yes ;
}
return len = = 0 ? 0 : - EINVAL ;
}
2005-08-12 19:51:49 +04:00
static int inet_diag_dump_sock ( struct sk_buff * skb , struct sock * sk ,
2005-04-17 02:20:36 +04:00
struct netlink_callback * cb )
{
2005-08-12 19:51:49 +04:00
struct inet_diag_req * r = NLMSG_DATA ( cb - > nlh ) ;
2005-04-17 02:20:36 +04:00
if ( cb - > nlh - > nlmsg_len > 4 + NLMSG_SPACE ( sizeof ( * r ) ) ) {
2005-08-12 19:51:49 +04:00
struct inet_diag_entry entry ;
2005-04-17 02:20:36 +04:00
struct rtattr * bc = ( struct rtattr * ) ( r + 1 ) ;
struct inet_sock * inet = inet_sk ( sk ) ;
entry . family = sk - > sk_family ;
2005-08-12 16:19:38 +04:00
# if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
2005-04-17 02:20:36 +04:00
if ( entry . family = = AF_INET6 ) {
struct ipv6_pinfo * np = inet6_sk ( sk ) ;
entry . saddr = np - > rcv_saddr . s6_addr32 ;
entry . daddr = np - > daddr . s6_addr32 ;
} else
# endif
{
entry . saddr = & inet - > rcv_saddr ;
entry . daddr = & inet - > daddr ;
}
entry . sport = inet - > num ;
entry . dport = ntohs ( inet - > dport ) ;
entry . userlocks = sk - > sk_userlocks ;
2005-08-12 19:51:49 +04:00
if ( ! inet_diag_bc_run ( RTA_DATA ( bc ) , RTA_PAYLOAD ( bc ) , & entry ) )
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-08-12 19:51:49 +04:00
return inet_diag_fill ( skb , sk , r - > idiag_ext , NETLINK_CB ( cb - > skb ) . pid ,
2005-08-10 12:54:28 +04:00
cb - > nlh - > nlmsg_seq , NLM_F_MULTI , cb - > nlh ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-12 19:51:49 +04:00
static int inet_diag_fill_req ( struct sk_buff * skb , struct sock * sk ,
2005-06-19 09:47:21 +04:00
struct request_sock * req ,
2005-08-10 12:54:28 +04:00
u32 pid , u32 seq ,
const struct nlmsghdr * unlh )
2005-04-17 02:20:36 +04:00
{
[NET] Generalise TCP's struct open_request minisock infrastructure
Kept this first changeset minimal, without changing existing names to
ease peer review.
Basicaly tcp_openreq_alloc now receives the or_calltable, that in turn
has two new members:
->slab, that replaces tcp_openreq_cachep
->obj_size, to inform the size of the openreq descendant for
a specific protocol
The protocol specific fields in struct open_request were moved to a
class hierarchy, with the things that are common to all connection
oriented PF_INET protocols in struct inet_request_sock, the TCP ones
in tcp_request_sock, that is an inet_request_sock, that is an
open_request.
I.e. this uses the same approach used for the struct sock class
hierarchy, with sk_prot indicating if the protocol wants to use the
open_request infrastructure by filling in sk_prot->rsk_prot with an
or_calltable.
Results? Performance is improved and TCP v4 now uses only 64 bytes per
open request minisock, down from 96 without this patch :-)
Next changeset will rename some of the structs, fields and functions
mentioned above, struct or_calltable is way unclear, better name it
struct request_sock_ops, s/struct open_request/struct request_sock/g,
etc.
Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-06-19 09:46:52 +04:00
const struct inet_request_sock * ireq = inet_rsk ( req ) ;
2005-04-17 02:20:36 +04:00
struct inet_sock * inet = inet_sk ( sk ) ;
unsigned char * b = skb - > tail ;
2005-08-12 19:51:49 +04:00
struct inet_diag_msg * r ;
2005-04-17 02:20:36 +04:00
struct nlmsghdr * nlh ;
long tmo ;
2005-08-10 12:54:28 +04:00
nlh = NLMSG_PUT ( skb , pid , seq , unlh - > nlmsg_type , sizeof ( * r ) ) ;
2005-04-17 02:20:36 +04:00
nlh - > nlmsg_flags = NLM_F_MULTI ;
r = NLMSG_DATA ( nlh ) ;
2005-08-12 19:51:49 +04:00
r - > idiag_family = sk - > sk_family ;
r - > idiag_state = TCP_SYN_RECV ;
r - > idiag_timer = 1 ;
r - > idiag_retrans = req - > retrans ;
2005-04-17 02:20:36 +04:00
2005-08-12 19:51:49 +04:00
r - > id . idiag_if = sk - > sk_bound_dev_if ;
r - > id . idiag_cookie [ 0 ] = ( u32 ) ( unsigned long ) req ;
r - > id . idiag_cookie [ 1 ] = ( u32 ) ( ( ( unsigned long ) req > > 31 ) > > 1 ) ;
2005-04-17 02:20:36 +04:00
tmo = req - > expires - jiffies ;
if ( tmo < 0 )
tmo = 0 ;
2005-08-12 19:51:49 +04:00
r - > id . idiag_sport = inet - > sport ;
r - > id . idiag_dport = ireq - > rmt_port ;
r - > id . idiag_src [ 0 ] = ireq - > loc_addr ;
r - > id . idiag_dst [ 0 ] = ireq - > rmt_addr ;
r - > idiag_expires = jiffies_to_msecs ( tmo ) ;
r - > idiag_rqueue = 0 ;
r - > idiag_wqueue = 0 ;
r - > idiag_uid = sock_i_uid ( sk ) ;
r - > idiag_inode = 0 ;
2005-08-12 16:19:38 +04:00
# if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
2005-08-12 19:51:49 +04:00
if ( r - > idiag_family = = AF_INET6 ) {
ipv6_addr_copy ( ( struct in6_addr * ) r - > id . idiag_src ,
[NET] Generalise TCP's struct open_request minisock infrastructure
Kept this first changeset minimal, without changing existing names to
ease peer review.
Basicaly tcp_openreq_alloc now receives the or_calltable, that in turn
has two new members:
->slab, that replaces tcp_openreq_cachep
->obj_size, to inform the size of the openreq descendant for
a specific protocol
The protocol specific fields in struct open_request were moved to a
class hierarchy, with the things that are common to all connection
oriented PF_INET protocols in struct inet_request_sock, the TCP ones
in tcp_request_sock, that is an inet_request_sock, that is an
open_request.
I.e. this uses the same approach used for the struct sock class
hierarchy, with sk_prot indicating if the protocol wants to use the
open_request infrastructure by filling in sk_prot->rsk_prot with an
or_calltable.
Results? Performance is improved and TCP v4 now uses only 64 bytes per
open request minisock, down from 96 without this patch :-)
Next changeset will rename some of the structs, fields and functions
mentioned above, struct or_calltable is way unclear, better name it
struct request_sock_ops, s/struct open_request/struct request_sock/g,
etc.
Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-06-19 09:46:52 +04:00
& tcp6_rsk ( req ) - > loc_addr ) ;
2005-08-12 19:51:49 +04:00
ipv6_addr_copy ( ( struct in6_addr * ) r - > id . idiag_dst ,
[NET] Generalise TCP's struct open_request minisock infrastructure
Kept this first changeset minimal, without changing existing names to
ease peer review.
Basicaly tcp_openreq_alloc now receives the or_calltable, that in turn
has two new members:
->slab, that replaces tcp_openreq_cachep
->obj_size, to inform the size of the openreq descendant for
a specific protocol
The protocol specific fields in struct open_request were moved to a
class hierarchy, with the things that are common to all connection
oriented PF_INET protocols in struct inet_request_sock, the TCP ones
in tcp_request_sock, that is an inet_request_sock, that is an
open_request.
I.e. this uses the same approach used for the struct sock class
hierarchy, with sk_prot indicating if the protocol wants to use the
open_request infrastructure by filling in sk_prot->rsk_prot with an
or_calltable.
Results? Performance is improved and TCP v4 now uses only 64 bytes per
open request minisock, down from 96 without this patch :-)
Next changeset will rename some of the structs, fields and functions
mentioned above, struct or_calltable is way unclear, better name it
struct request_sock_ops, s/struct open_request/struct request_sock/g,
etc.
Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-06-19 09:46:52 +04:00
& tcp6_rsk ( req ) - > rmt_addr ) ;
2005-04-17 02:20:36 +04:00
}
# endif
nlh - > nlmsg_len = skb - > tail - b ;
return skb - > len ;
nlmsg_failure :
skb_trim ( skb , b - skb - > data ) ;
return - 1 ;
}
2005-08-12 19:51:49 +04:00
static int inet_diag_dump_reqs ( struct sk_buff * skb , struct sock * sk ,
2005-04-17 02:20:36 +04:00
struct netlink_callback * cb )
{
2005-08-12 19:51:49 +04:00
struct inet_diag_entry entry ;
struct inet_diag_req * r = NLMSG_DATA ( cb - > nlh ) ;
2005-08-10 07:10:42 +04:00
struct inet_connection_sock * icsk = inet_csk ( sk ) ;
2005-06-19 09:48:55 +04:00
struct listen_sock * lopt ;
2005-04-17 02:20:36 +04:00
struct rtattr * bc = NULL ;
struct inet_sock * inet = inet_sk ( sk ) ;
int j , s_j ;
int reqnum , s_reqnum ;
int err = 0 ;
s_j = cb - > args [ 3 ] ;
s_reqnum = cb - > args [ 4 ] ;
if ( s_j > 0 )
s_j - - ;
entry . family = sk - > sk_family ;
2005-08-10 07:10:42 +04:00
read_lock_bh ( & icsk - > icsk_accept_queue . syn_wait_lock ) ;
2005-04-17 02:20:36 +04:00
2005-08-10 07:10:42 +04:00
lopt = icsk - > icsk_accept_queue . listen_opt ;
2005-04-17 02:20:36 +04:00
if ( ! lopt | | ! lopt - > qlen )
goto out ;
if ( cb - > nlh - > nlmsg_len > 4 + NLMSG_SPACE ( sizeof ( * r ) ) ) {
bc = ( struct rtattr * ) ( r + 1 ) ;
entry . sport = inet - > num ;
entry . userlocks = sk - > sk_userlocks ;
}
2005-08-10 12:54:28 +04:00
for ( j = s_j ; j < lopt - > nr_table_entries ; j + + ) {
2005-06-19 09:47:21 +04:00
struct request_sock * req , * head = lopt - > syn_table [ j ] ;
2005-04-17 02:20:36 +04:00
reqnum = 0 ;
for ( req = head ; req ; reqnum + + , req = req - > dl_next ) {
[NET] Generalise TCP's struct open_request minisock infrastructure
Kept this first changeset minimal, without changing existing names to
ease peer review.
Basicaly tcp_openreq_alloc now receives the or_calltable, that in turn
has two new members:
->slab, that replaces tcp_openreq_cachep
->obj_size, to inform the size of the openreq descendant for
a specific protocol
The protocol specific fields in struct open_request were moved to a
class hierarchy, with the things that are common to all connection
oriented PF_INET protocols in struct inet_request_sock, the TCP ones
in tcp_request_sock, that is an inet_request_sock, that is an
open_request.
I.e. this uses the same approach used for the struct sock class
hierarchy, with sk_prot indicating if the protocol wants to use the
open_request infrastructure by filling in sk_prot->rsk_prot with an
or_calltable.
Results? Performance is improved and TCP v4 now uses only 64 bytes per
open request minisock, down from 96 without this patch :-)
Next changeset will rename some of the structs, fields and functions
mentioned above, struct or_calltable is way unclear, better name it
struct request_sock_ops, s/struct open_request/struct request_sock/g,
etc.
Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-06-19 09:46:52 +04:00
struct inet_request_sock * ireq = inet_rsk ( req ) ;
2005-04-17 02:20:36 +04:00
if ( reqnum < s_reqnum )
continue ;
2005-08-12 19:51:49 +04:00
if ( r - > id . idiag_dport ! = ireq - > rmt_port & &
r - > id . idiag_dport )
2005-04-17 02:20:36 +04:00
continue ;
if ( bc ) {
entry . saddr =
2005-08-12 16:19:38 +04:00
# if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
2005-04-17 02:20:36 +04:00
( entry . family = = AF_INET6 ) ?
[NET] Generalise TCP's struct open_request minisock infrastructure
Kept this first changeset minimal, without changing existing names to
ease peer review.
Basicaly tcp_openreq_alloc now receives the or_calltable, that in turn
has two new members:
->slab, that replaces tcp_openreq_cachep
->obj_size, to inform the size of the openreq descendant for
a specific protocol
The protocol specific fields in struct open_request were moved to a
class hierarchy, with the things that are common to all connection
oriented PF_INET protocols in struct inet_request_sock, the TCP ones
in tcp_request_sock, that is an inet_request_sock, that is an
open_request.
I.e. this uses the same approach used for the struct sock class
hierarchy, with sk_prot indicating if the protocol wants to use the
open_request infrastructure by filling in sk_prot->rsk_prot with an
or_calltable.
Results? Performance is improved and TCP v4 now uses only 64 bytes per
open request minisock, down from 96 without this patch :-)
Next changeset will rename some of the structs, fields and functions
mentioned above, struct or_calltable is way unclear, better name it
struct request_sock_ops, s/struct open_request/struct request_sock/g,
etc.
Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-06-19 09:46:52 +04:00
tcp6_rsk ( req ) - > loc_addr . s6_addr32 :
2005-04-17 02:20:36 +04:00
# endif
[NET] Generalise TCP's struct open_request minisock infrastructure
Kept this first changeset minimal, without changing existing names to
ease peer review.
Basicaly tcp_openreq_alloc now receives the or_calltable, that in turn
has two new members:
->slab, that replaces tcp_openreq_cachep
->obj_size, to inform the size of the openreq descendant for
a specific protocol
The protocol specific fields in struct open_request were moved to a
class hierarchy, with the things that are common to all connection
oriented PF_INET protocols in struct inet_request_sock, the TCP ones
in tcp_request_sock, that is an inet_request_sock, that is an
open_request.
I.e. this uses the same approach used for the struct sock class
hierarchy, with sk_prot indicating if the protocol wants to use the
open_request infrastructure by filling in sk_prot->rsk_prot with an
or_calltable.
Results? Performance is improved and TCP v4 now uses only 64 bytes per
open request minisock, down from 96 without this patch :-)
Next changeset will rename some of the structs, fields and functions
mentioned above, struct or_calltable is way unclear, better name it
struct request_sock_ops, s/struct open_request/struct request_sock/g,
etc.
Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-06-19 09:46:52 +04:00
& ireq - > loc_addr ;
2005-04-17 02:20:36 +04:00
entry . daddr =
2005-08-12 16:19:38 +04:00
# if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
2005-04-17 02:20:36 +04:00
( entry . family = = AF_INET6 ) ?
[NET] Generalise TCP's struct open_request minisock infrastructure
Kept this first changeset minimal, without changing existing names to
ease peer review.
Basicaly tcp_openreq_alloc now receives the or_calltable, that in turn
has two new members:
->slab, that replaces tcp_openreq_cachep
->obj_size, to inform the size of the openreq descendant for
a specific protocol
The protocol specific fields in struct open_request were moved to a
class hierarchy, with the things that are common to all connection
oriented PF_INET protocols in struct inet_request_sock, the TCP ones
in tcp_request_sock, that is an inet_request_sock, that is an
open_request.
I.e. this uses the same approach used for the struct sock class
hierarchy, with sk_prot indicating if the protocol wants to use the
open_request infrastructure by filling in sk_prot->rsk_prot with an
or_calltable.
Results? Performance is improved and TCP v4 now uses only 64 bytes per
open request minisock, down from 96 without this patch :-)
Next changeset will rename some of the structs, fields and functions
mentioned above, struct or_calltable is way unclear, better name it
struct request_sock_ops, s/struct open_request/struct request_sock/g,
etc.
Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-06-19 09:46:52 +04:00
tcp6_rsk ( req ) - > rmt_addr . s6_addr32 :
2005-04-17 02:20:36 +04:00
# endif
[NET] Generalise TCP's struct open_request minisock infrastructure
Kept this first changeset minimal, without changing existing names to
ease peer review.
Basicaly tcp_openreq_alloc now receives the or_calltable, that in turn
has two new members:
->slab, that replaces tcp_openreq_cachep
->obj_size, to inform the size of the openreq descendant for
a specific protocol
The protocol specific fields in struct open_request were moved to a
class hierarchy, with the things that are common to all connection
oriented PF_INET protocols in struct inet_request_sock, the TCP ones
in tcp_request_sock, that is an inet_request_sock, that is an
open_request.
I.e. this uses the same approach used for the struct sock class
hierarchy, with sk_prot indicating if the protocol wants to use the
open_request infrastructure by filling in sk_prot->rsk_prot with an
or_calltable.
Results? Performance is improved and TCP v4 now uses only 64 bytes per
open request minisock, down from 96 without this patch :-)
Next changeset will rename some of the structs, fields and functions
mentioned above, struct or_calltable is way unclear, better name it
struct request_sock_ops, s/struct open_request/struct request_sock/g,
etc.
Signed-off-by: Arnaldo Carvalho de Melo <acme@ghostprotocols.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-06-19 09:46:52 +04:00
& ireq - > rmt_addr ;
entry . dport = ntohs ( ireq - > rmt_port ) ;
2005-04-17 02:20:36 +04:00
2005-08-12 19:51:49 +04:00
if ( ! inet_diag_bc_run ( RTA_DATA ( bc ) ,
2005-04-17 02:20:36 +04:00
RTA_PAYLOAD ( bc ) , & entry ) )
continue ;
}
2005-08-12 19:51:49 +04:00
err = inet_diag_fill_req ( skb , sk , req ,
2005-04-17 02:20:36 +04:00
NETLINK_CB ( cb - > skb ) . pid ,
2005-08-10 12:54:28 +04:00
cb - > nlh - > nlmsg_seq , cb - > nlh ) ;
2005-04-17 02:20:36 +04:00
if ( err < 0 ) {
cb - > args [ 3 ] = j + 1 ;
cb - > args [ 4 ] = reqnum ;
goto out ;
}
}
s_reqnum = 0 ;
}
out :
2005-08-10 07:10:42 +04:00
read_unlock_bh ( & icsk - > icsk_accept_queue . syn_wait_lock ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2005-08-12 19:51:49 +04:00
static int inet_diag_dump ( struct sk_buff * skb , struct netlink_callback * cb )
2005-04-17 02:20:36 +04:00
{
int i , num ;
int s_i , s_num ;
2005-08-12 19:51:49 +04:00
struct inet_diag_req * r = NLMSG_DATA ( cb - > nlh ) ;
2005-08-12 16:27:49 +04:00
const struct inet_diag_handler * handler ;
2005-08-10 12:54:28 +04:00
struct inet_hashinfo * hashinfo ;
2005-04-17 02:20:36 +04:00
2005-08-12 16:27:49 +04:00
handler = inet_diag_table [ cb - > nlh - > nlmsg_type ] ;
BUG_ON ( handler = = NULL ) ;
hashinfo = handler - > idiag_hashinfo ;
2005-04-17 02:20:36 +04:00
s_i = cb - > args [ 1 ] ;
s_num = num = cb - > args [ 2 ] ;
2005-08-12 16:27:49 +04:00
2005-04-17 02:20:36 +04:00
if ( cb - > args [ 0 ] = = 0 ) {
2005-08-12 19:51:49 +04:00
if ( ! ( r - > idiag_states & ( TCPF_LISTEN | TCPF_SYN_RECV ) ) )
2005-04-17 02:20:36 +04:00
goto skip_listen_ht ;
2005-08-10 12:54:28 +04:00
inet_listen_lock ( hashinfo ) ;
2005-08-10 06:59:44 +04:00
for ( i = s_i ; i < INET_LHTABLE_SIZE ; i + + ) {
2005-04-17 02:20:36 +04:00
struct sock * sk ;
struct hlist_node * node ;
num = 0 ;
2005-08-10 12:54:28 +04:00
sk_for_each ( sk , node , & hashinfo - > listening_hash [ i ] ) {
2005-04-17 02:20:36 +04:00
struct inet_sock * inet = inet_sk ( sk ) ;
if ( num < s_num ) {
num + + ;
continue ;
}
2005-08-12 19:51:49 +04:00
if ( r - > id . idiag_sport ! = inet - > sport & &
r - > id . idiag_sport )
2005-04-17 02:20:36 +04:00
goto next_listen ;
2005-08-12 19:51:49 +04:00
if ( ! ( r - > idiag_states & TCPF_LISTEN ) | |
r - > id . idiag_dport | |
2005-04-17 02:20:36 +04:00
cb - > args [ 3 ] > 0 )
goto syn_recv ;
2005-08-12 19:51:49 +04:00
if ( inet_diag_dump_sock ( skb , sk , cb ) < 0 ) {
2005-08-10 12:54:28 +04:00
inet_listen_unlock ( hashinfo ) ;
2005-04-17 02:20:36 +04:00
goto done ;
}
syn_recv :
2005-08-12 19:51:49 +04:00
if ( ! ( r - > idiag_states & TCPF_SYN_RECV ) )
2005-04-17 02:20:36 +04:00
goto next_listen ;
2005-08-12 19:51:49 +04:00
if ( inet_diag_dump_reqs ( skb , sk , cb ) < 0 ) {
2005-08-10 12:54:28 +04:00
inet_listen_unlock ( hashinfo ) ;
2005-04-17 02:20:36 +04:00
goto done ;
}
next_listen :
cb - > args [ 3 ] = 0 ;
cb - > args [ 4 ] = 0 ;
+ + num ;
}
s_num = 0 ;
cb - > args [ 3 ] = 0 ;
cb - > args [ 4 ] = 0 ;
}
2005-08-10 12:54:28 +04:00
inet_listen_unlock ( hashinfo ) ;
2005-04-17 02:20:36 +04:00
skip_listen_ht :
cb - > args [ 0 ] = 1 ;
s_i = num = s_num = 0 ;
}
2005-08-12 19:51:49 +04:00
if ( ! ( r - > idiag_states & ~ ( TCPF_LISTEN | TCPF_SYN_RECV ) ) )
2005-04-17 02:20:36 +04:00
return skb - > len ;
2005-08-10 12:54:28 +04:00
for ( i = s_i ; i < hashinfo - > ehash_size ; i + + ) {
struct inet_ehash_bucket * head = & hashinfo - > ehash [ i ] ;
2005-04-17 02:20:36 +04:00
struct sock * sk ;
struct hlist_node * node ;
if ( i > s_i )
s_num = 0 ;
read_lock_bh ( & head - > lock ) ;
num = 0 ;
sk_for_each ( sk , node , & head - > chain ) {
struct inet_sock * inet = inet_sk ( sk ) ;
if ( num < s_num )
goto next_normal ;
2005-08-12 19:51:49 +04:00
if ( ! ( r - > idiag_states & ( 1 < < sk - > sk_state ) ) )
2005-04-17 02:20:36 +04:00
goto next_normal ;
2005-08-12 19:51:49 +04:00
if ( r - > id . idiag_sport ! = inet - > sport & &
r - > id . idiag_sport )
2005-04-17 02:20:36 +04:00
goto next_normal ;
2005-08-12 19:51:49 +04:00
if ( r - > id . idiag_dport ! = inet - > dport & & r - > id . idiag_dport )
2005-04-17 02:20:36 +04:00
goto next_normal ;
2005-08-12 19:51:49 +04:00
if ( inet_diag_dump_sock ( skb , sk , cb ) < 0 ) {
2005-04-17 02:20:36 +04:00
read_unlock_bh ( & head - > lock ) ;
goto done ;
}
next_normal :
+ + num ;
}
2005-08-12 19:51:49 +04:00
if ( r - > idiag_states & TCPF_TIME_WAIT ) {
2005-04-17 02:20:36 +04:00
sk_for_each ( sk , node ,
2005-08-10 12:54:28 +04:00
& hashinfo - > ehash [ i + hashinfo - > ehash_size ] . chain ) {
2005-04-17 02:20:36 +04:00
struct inet_sock * inet = inet_sk ( sk ) ;
if ( num < s_num )
goto next_dying ;
2005-08-12 19:51:49 +04:00
if ( r - > id . idiag_sport ! = inet - > sport & &
r - > id . idiag_sport )
2005-04-17 02:20:36 +04:00
goto next_dying ;
2005-08-12 19:51:49 +04:00
if ( r - > id . idiag_dport ! = inet - > dport & &
r - > id . idiag_dport )
2005-04-17 02:20:36 +04:00
goto next_dying ;
2005-08-12 19:51:49 +04:00
if ( inet_diag_dump_sock ( skb , sk , cb ) < 0 ) {
2005-04-17 02:20:36 +04:00
read_unlock_bh ( & head - > lock ) ;
goto done ;
}
next_dying :
+ + num ;
}
}
read_unlock_bh ( & head - > lock ) ;
}
done :
cb - > args [ 1 ] = i ;
cb - > args [ 2 ] = num ;
return skb - > len ;
}
static __inline__ int
2005-08-12 19:51:49 +04:00
inet_diag_rcv_msg ( struct sk_buff * skb , struct nlmsghdr * nlh )
2005-04-17 02:20:36 +04:00
{
if ( ! ( nlh - > nlmsg_flags & NLM_F_REQUEST ) )
return 0 ;
2005-08-12 16:27:49 +04:00
if ( nlh - > nlmsg_type > = INET_DIAG_GETSOCK_MAX )
2005-04-17 02:20:36 +04:00
goto err_inval ;
2005-08-12 16:27:49 +04:00
if ( inet_diag_table [ nlh - > nlmsg_type ] = = NULL )
return - ENOENT ;
2005-08-12 19:51:49 +04:00
if ( NLMSG_LENGTH ( sizeof ( struct inet_diag_req ) ) > skb - > len )
2005-04-17 02:20:36 +04:00
goto err_inval ;
if ( nlh - > nlmsg_flags & NLM_F_DUMP ) {
2005-08-12 19:51:49 +04:00
if ( nlh - > nlmsg_len >
( 4 + NLMSG_SPACE ( sizeof ( struct inet_diag_req ) ) ) ) {
struct rtattr * rta = ( void * ) ( NLMSG_DATA ( nlh ) +
sizeof ( struct inet_diag_req ) ) ;
if ( rta - > rta_type ! = INET_DIAG_REQ_BYTECODE | |
2005-04-17 02:20:36 +04:00
rta - > rta_len < 8 | |
2005-08-12 19:51:49 +04:00
rta - > rta_len >
( nlh - > nlmsg_len -
NLMSG_SPACE ( sizeof ( struct inet_diag_req ) ) ) )
2005-04-17 02:20:36 +04:00
goto err_inval ;
2005-08-12 19:51:49 +04:00
if ( inet_diag_bc_audit ( RTA_DATA ( rta ) , RTA_PAYLOAD ( rta ) ) )
2005-04-17 02:20:36 +04:00
goto err_inval ;
}
2005-08-12 19:51:49 +04:00
return netlink_dump_start ( idiagnl , skb , nlh ,
2005-11-10 04:25:52 +03:00
inet_diag_dump , NULL ) ;
2005-04-17 02:20:36 +04:00
} else {
2005-08-12 19:51:49 +04:00
return inet_diag_get_exact ( skb , nlh ) ;
2005-04-17 02:20:36 +04:00
}
err_inval :
return - EINVAL ;
}
2005-08-12 19:51:49 +04:00
static inline void inet_diag_rcv_skb ( struct sk_buff * skb )
2005-04-17 02:20:36 +04:00
{
int err ;
struct nlmsghdr * nlh ;
if ( skb - > len > = NLMSG_SPACE ( 0 ) ) {
nlh = ( struct nlmsghdr * ) skb - > data ;
if ( nlh - > nlmsg_len < sizeof ( * nlh ) | | skb - > len < nlh - > nlmsg_len )
return ;
2005-08-12 19:51:49 +04:00
err = inet_diag_rcv_msg ( skb , nlh ) ;
2005-04-17 02:20:36 +04:00
if ( err | | nlh - > nlmsg_flags & NLM_F_ACK )
netlink_ack ( skb , nlh , err ) ;
}
}
2005-08-12 19:51:49 +04:00
static void inet_diag_rcv ( struct sock * sk , int len )
2005-04-17 02:20:36 +04:00
{
struct sk_buff * skb ;
2005-05-04 01:55:09 +04:00
unsigned int qlen = skb_queue_len ( & sk - > sk_receive_queue ) ;
2005-04-17 02:20:36 +04:00
2005-05-04 01:55:09 +04:00
while ( qlen - - & & ( skb = skb_dequeue ( & sk - > sk_receive_queue ) ) ) {
2005-08-12 19:51:49 +04:00
inet_diag_rcv_skb ( skb ) ;
2005-04-17 02:20:36 +04:00
kfree_skb ( skb ) ;
}
}
2005-08-12 16:27:49 +04:00
static DEFINE_SPINLOCK ( inet_diag_register_lock ) ;
int inet_diag_register ( const struct inet_diag_handler * h )
{
const __u16 type = h - > idiag_type ;
int err = - EINVAL ;
if ( type > = INET_DIAG_GETSOCK_MAX )
goto out ;
spin_lock ( & inet_diag_register_lock ) ;
err = - EEXIST ;
if ( inet_diag_table [ type ] = = NULL ) {
inet_diag_table [ type ] = h ;
err = 0 ;
}
spin_unlock ( & inet_diag_register_lock ) ;
out :
return err ;
}
EXPORT_SYMBOL_GPL ( inet_diag_register ) ;
void inet_diag_unregister ( const struct inet_diag_handler * h )
{
const __u16 type = h - > idiag_type ;
if ( type > = INET_DIAG_GETSOCK_MAX )
return ;
spin_lock ( & inet_diag_register_lock ) ;
inet_diag_table [ type ] = NULL ;
spin_unlock ( & inet_diag_register_lock ) ;
synchronize_rcu ( ) ;
}
EXPORT_SYMBOL_GPL ( inet_diag_unregister ) ;
2005-08-12 19:51:49 +04:00
static int __init inet_diag_init ( void )
2005-04-17 02:20:36 +04:00
{
2005-08-12 16:27:49 +04:00
const int inet_diag_table_size = ( INET_DIAG_GETSOCK_MAX *
sizeof ( struct inet_diag_handler * ) ) ;
int err = - ENOMEM ;
inet_diag_table = kmalloc ( inet_diag_table_size , GFP_KERNEL ) ;
if ( ! inet_diag_table )
goto out ;
memset ( inet_diag_table , 0 , inet_diag_table_size ) ;
2005-08-15 23:33:26 +04:00
idiagnl = netlink_kernel_create ( NETLINK_INET_DIAG , 0 , inet_diag_rcv ,
2005-08-12 19:51:49 +04:00
THIS_MODULE ) ;
if ( idiagnl = = NULL )
2005-08-12 16:27:49 +04:00
goto out_free_table ;
[INET_DIAG]: Move the tcp_diag interface to the proper place
With this the previous setup is back, i.e. tcp_diag can be built as a module,
as dccp_diag and both share the infrastructure available in inet_diag.
If one selects CONFIG_INET_DIAG as module CONFIG_INET_TCP_DIAG will also be
built as a module, as will CONFIG_INET_DCCP_DIAG, if CONFIG_IP_DCCP was
selected static or as a module, if CONFIG_INET_DIAG is y, being statically
linked CONFIG_INET_TCP_DIAG will follow suit and CONFIG_INET_DCCP_DIAG will be
built in the same manner as CONFIG_IP_DCCP.
Now to aim at UDP, converting it to use inet_hashinfo, so that we can use
iproute2 for UDP sockets as well.
Ah, just to show an example of this new infrastructure working for DCCP :-)
[root@qemu ~]# ./ss -dane
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 0 *:5001 *:* ino:942 sk:cfd503a0
ESTAB 0 0 127.0.0.1:5001 127.0.0.1:32770 ino:943 sk:cfd50a60
ESTAB 0 0 127.0.0.1:32770 127.0.0.1:5001 ino:947 sk:cfd50700
TIME-WAIT 0 0 127.0.0.1:32769 127.0.0.1:5001 timer:(timewait,3.430ms,0) ino:0 sk:cf209620
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2005-08-12 19:59:17 +04:00
err = 0 ;
2005-08-12 16:27:49 +04:00
out :
return err ;
out_free_table :
kfree ( inet_diag_table ) ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2005-08-12 19:51:49 +04:00
static void __exit inet_diag_exit ( void )
2005-04-17 02:20:36 +04:00
{
2005-08-12 19:51:49 +04:00
sock_release ( idiagnl - > sk_socket ) ;
2005-08-12 16:27:49 +04:00
kfree ( inet_diag_table ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-12 19:51:49 +04:00
module_init ( inet_diag_init ) ;
module_exit ( inet_diag_exit ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;