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>
# include <linux/inet_diag.h>
# include <linux/sock_diag.h>
static struct sock_diag_handler * sock_diag_handlers [ AF_MAX ] ;
static int ( * inet_rcv_compat ) ( struct sk_buff * skb , struct nlmsghdr * nlh ) ;
static DEFINE_MUTEX ( sock_diag_table_mutex ) ;
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 ) ;
int sock_diag_register ( struct sock_diag_handler * hndl )
{
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 ) ;
void sock_diag_unregister ( struct sock_diag_handler * hnld )
{
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 ) ;
static inline struct sock_diag_handler * sock_diag_lock_handler ( int family )
{
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 ] ;
}
static inline void sock_diag_unlock_handler ( struct sock_diag_handler * h )
{
mutex_unlock ( & sock_diag_table_mutex ) ;
}
static int __sock_diag_rcv_msg ( struct sk_buff * skb , struct nlmsghdr * nlh )
{
int err ;
struct sock_diag_req * req = NLMSG_DATA ( nlh ) ;
struct sock_diag_handler * hndl ;
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 ) ;
}
struct sock * sock_diag_nlsk ;
EXPORT_SYMBOL_GPL ( sock_diag_nlsk ) ;
static int __init sock_diag_init ( void )
{
sock_diag_nlsk = netlink_kernel_create ( & init_net , NETLINK_SOCK_DIAG , 0 ,
sock_diag_rcv , NULL , THIS_MODULE ) ;
return sock_diag_nlsk = = NULL ? - ENOMEM : 0 ;
}
static void __exit sock_diag_exit ( void )
{
netlink_kernel_release ( sock_diag_nlsk ) ;
}
module_init ( sock_diag_init ) ;
module_exit ( sock_diag_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_NET_PF_PROTO ( PF_NETLINK , NETLINK_SOCK_DIAG ) ;