2011-12-06 11:59:52 +04:00
# include <linux/mutex.h>
# include <linux/socket.h>
# include <linux/skbuff.h>
# include <net/netlink.h>
# include <net/net_namespace.h>
# include <linux/module.h>
2011-12-30 04:53:13 +04:00
# include <net/sock.h>
2011-12-06 11:59:52 +04:00
# include <linux/inet_diag.h>
# include <linux/sock_diag.h>
2012-04-24 22:21:07 +04:00
static const struct sock_diag_handler * sock_diag_handlers [ AF_MAX ] ;
2011-12-06 11:59:52 +04:00
static int ( * inet_rcv_compat ) ( struct sk_buff * skb , struct nlmsghdr * nlh ) ;
static DEFINE_MUTEX ( sock_diag_table_mutex ) ;
2011-12-15 06:43:44 +04:00
int sock_diag_check_cookie ( void * sk , __u32 * cookie )
{
if ( ( cookie [ 0 ] ! = INET_DIAG_NOCOOKIE | |
cookie [ 1 ] ! = INET_DIAG_NOCOOKIE ) & &
( ( u32 ) ( unsigned long ) sk ! = cookie [ 0 ] | |
( u32 ) ( ( ( ( unsigned long ) sk ) > > 31 ) > > 1 ) ! = cookie [ 1 ] ) )
return - ESTALE ;
else
return 0 ;
}
EXPORT_SYMBOL_GPL ( sock_diag_check_cookie ) ;
void sock_diag_save_cookie ( void * sk , __u32 * cookie )
{
cookie [ 0 ] = ( u32 ) ( unsigned long ) sk ;
cookie [ 1 ] = ( u32 ) ( ( ( unsigned long ) sk > > 31 ) > > 1 ) ;
}
EXPORT_SYMBOL_GPL ( sock_diag_save_cookie ) ;
2011-12-30 04:53:13 +04:00
int sock_diag_put_meminfo ( struct sock * sk , struct sk_buff * skb , int attrtype )
{
2012-06-27 03:36:11 +04:00
u32 mem [ SK_MEMINFO_VARS ] ;
2011-12-30 04:53:13 +04:00
mem [ SK_MEMINFO_RMEM_ALLOC ] = sk_rmem_alloc_get ( sk ) ;
mem [ SK_MEMINFO_RCVBUF ] = sk - > sk_rcvbuf ;
mem [ SK_MEMINFO_WMEM_ALLOC ] = sk_wmem_alloc_get ( sk ) ;
mem [ SK_MEMINFO_SNDBUF ] = sk - > sk_sndbuf ;
mem [ SK_MEMINFO_FWD_ALLOC ] = sk - > sk_forward_alloc ;
mem [ SK_MEMINFO_WMEM_QUEUED ] = sk - > sk_wmem_queued ;
mem [ SK_MEMINFO_OPTMEM ] = atomic_read ( & sk - > sk_omem_alloc ) ;
2012-06-04 07:50:35 +04:00
mem [ SK_MEMINFO_BACKLOG ] = sk - > sk_backlog . len ;
2011-12-30 04:53:13 +04:00
2012-06-27 03:36:11 +04:00
return nla_put ( skb , attrtype , sizeof ( mem ) , & mem ) ;
2011-12-30 04:53:13 +04:00
}
EXPORT_SYMBOL_GPL ( sock_diag_put_meminfo ) ;
2011-12-06 11:59:52 +04:00
void sock_diag_register_inet_compat ( int ( * fn ) ( struct sk_buff * skb , struct nlmsghdr * nlh ) )
{
mutex_lock ( & sock_diag_table_mutex ) ;
inet_rcv_compat = fn ;
mutex_unlock ( & sock_diag_table_mutex ) ;
}
EXPORT_SYMBOL_GPL ( sock_diag_register_inet_compat ) ;
void sock_diag_unregister_inet_compat ( int ( * fn ) ( struct sk_buff * skb , struct nlmsghdr * nlh ) )
{
mutex_lock ( & sock_diag_table_mutex ) ;
inet_rcv_compat = NULL ;
mutex_unlock ( & sock_diag_table_mutex ) ;
}
EXPORT_SYMBOL_GPL ( sock_diag_unregister_inet_compat ) ;
2012-04-24 22:21:07 +04:00
int sock_diag_register ( const struct sock_diag_handler * hndl )
2011-12-06 11:59:52 +04:00
{
int err = 0 ;
2011-12-08 00:49:38 +04:00
if ( hndl - > family > = AF_MAX )
2011-12-06 11:59:52 +04:00
return - EINVAL ;
mutex_lock ( & sock_diag_table_mutex ) ;
if ( sock_diag_handlers [ hndl - > family ] )
err = - EBUSY ;
else
sock_diag_handlers [ hndl - > family ] = hndl ;
mutex_unlock ( & sock_diag_table_mutex ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( sock_diag_register ) ;
2012-04-24 22:21:07 +04:00
void sock_diag_unregister ( const struct sock_diag_handler * hnld )
2011-12-06 11:59:52 +04:00
{
int family = hnld - > family ;
2011-12-08 00:49:38 +04:00
if ( family > = AF_MAX )
2011-12-06 11:59:52 +04:00
return ;
mutex_lock ( & sock_diag_table_mutex ) ;
BUG_ON ( sock_diag_handlers [ family ] ! = hnld ) ;
sock_diag_handlers [ family ] = NULL ;
mutex_unlock ( & sock_diag_table_mutex ) ;
}
EXPORT_SYMBOL_GPL ( sock_diag_unregister ) ;
2012-04-24 22:21:07 +04:00
static const inline struct sock_diag_handler * sock_diag_lock_handler ( int family )
2011-12-06 11:59:52 +04:00
{
if ( sock_diag_handlers [ family ] = = NULL )
request_module ( " net-pf-%d-proto-%d-type-%d " , PF_NETLINK ,
2011-12-15 06:43:27 +04:00
NETLINK_SOCK_DIAG , family ) ;
2011-12-06 11:59:52 +04:00
mutex_lock ( & sock_diag_table_mutex ) ;
return sock_diag_handlers [ family ] ;
}
2012-04-24 22:21:07 +04:00
static inline void sock_diag_unlock_handler ( const struct sock_diag_handler * h )
2011-12-06 11:59:52 +04:00
{
mutex_unlock ( & sock_diag_table_mutex ) ;
}
static int __sock_diag_rcv_msg ( struct sk_buff * skb , struct nlmsghdr * nlh )
{
int err ;
2012-06-27 03:36:11 +04:00
struct sock_diag_req * req = nlmsg_data ( nlh ) ;
2012-04-24 22:21:07 +04:00
const struct sock_diag_handler * hndl ;
2011-12-06 11:59:52 +04:00
if ( nlmsg_len ( nlh ) < sizeof ( * req ) )
return - EINVAL ;
hndl = sock_diag_lock_handler ( req - > sdiag_family ) ;
if ( hndl = = NULL )
err = - ENOENT ;
else
err = hndl - > dump ( skb , nlh ) ;
sock_diag_unlock_handler ( hndl ) ;
return err ;
}
static int sock_diag_rcv_msg ( struct sk_buff * skb , struct nlmsghdr * nlh )
{
int ret ;
switch ( nlh - > nlmsg_type ) {
case TCPDIAG_GETSOCK :
case DCCPDIAG_GETSOCK :
if ( inet_rcv_compat = = NULL )
request_module ( " net-pf-%d-proto-%d-type-%d " , PF_NETLINK ,
2011-12-15 06:43:27 +04:00
NETLINK_SOCK_DIAG , AF_INET ) ;
2011-12-06 11:59:52 +04:00
mutex_lock ( & sock_diag_table_mutex ) ;
if ( inet_rcv_compat ! = NULL )
ret = inet_rcv_compat ( skb , nlh ) ;
else
ret = - EOPNOTSUPP ;
mutex_unlock ( & sock_diag_table_mutex ) ;
return ret ;
case SOCK_DIAG_BY_FAMILY :
return __sock_diag_rcv_msg ( skb , nlh ) ;
default :
return - EINVAL ;
}
}
static DEFINE_MUTEX ( sock_diag_mutex ) ;
static void sock_diag_rcv ( struct sk_buff * skb )
{
mutex_lock ( & sock_diag_mutex ) ;
netlink_rcv_skb ( skb , & sock_diag_rcv_msg ) ;
mutex_unlock ( & sock_diag_mutex ) ;
}
2012-07-16 08:28:49 +04:00
static int __net_init diag_net_init ( struct net * net )
2011-12-06 11:59:52 +04:00
{
2012-06-29 10:15:21 +04:00
struct netlink_kernel_cfg cfg = {
. input = sock_diag_rcv ,
} ;
2012-09-08 06:53:54 +04:00
net - > diag_nlsk = netlink_kernel_create ( net , NETLINK_SOCK_DIAG , & cfg ) ;
2012-07-16 08:28:49 +04:00
return net - > diag_nlsk = = NULL ? - ENOMEM : 0 ;
}
static void __net_exit diag_net_exit ( struct net * net )
{
netlink_kernel_release ( net - > diag_nlsk ) ;
net - > diag_nlsk = NULL ;
}
static struct pernet_operations diag_net_ops = {
. init = diag_net_init ,
. exit = diag_net_exit ,
} ;
static int __init sock_diag_init ( void )
{
return register_pernet_subsys ( & diag_net_ops ) ;
2011-12-06 11:59:52 +04:00
}
static void __exit sock_diag_exit ( void )
{
2012-07-16 08:28:49 +04:00
unregister_pernet_subsys ( & diag_net_ops ) ;
2011-12-06 11:59:52 +04:00
}
module_init ( sock_diag_init ) ;
module_exit ( sock_diag_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_NET_PF_PROTO ( PF_NETLINK , NETLINK_SOCK_DIAG ) ;