2011-12-09 10:23:51 +04:00
/*
* udp_diag . c Module for monitoring UDP transport protocols sockets .
*
* Authors : Pavel Emelyanov , < xemul @ parallels . com >
*
* 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/module.h>
# include <linux/inet_diag.h>
# include <linux/udp.h>
# include <net/udp.h>
# include <net/udplite.h>
# include <linux/sock_diag.h>
2011-12-09 10:24:21 +04:00
static int sk_diag_dump ( struct sock * sk , struct sk_buff * skb ,
2015-03-10 17:15:54 +03:00
struct netlink_callback * cb ,
const struct inet_diag_req_v2 * req ,
2016-09-07 18:42:25 +03:00
struct nlattr * bc , bool net_admin )
2011-12-09 10:24:21 +04:00
{
if ( ! inet_diag_bc_sk ( bc , sk ) )
return 0 ;
2012-05-25 03:58:08 +04:00
return inet_sk_diag_fill ( sk , NULL , skb , req ,
2013-04-17 10:46:57 +04:00
sk_user_ns ( NETLINK_CB ( cb - > skb ) . sk ) ,
2012-09-08 00:12:54 +04:00
NETLINK_CB ( cb - > skb ) . portid ,
2016-09-07 18:42:25 +03:00
cb - > nlh - > nlmsg_seq , NLM_F_MULTI , cb - > nlh , net_admin ) ;
2011-12-09 10:24:21 +04:00
}
2011-12-09 10:23:51 +04:00
static int udp_dump_one ( struct udp_table * tbl , struct sk_buff * in_skb ,
2015-03-10 17:15:54 +03:00
const struct nlmsghdr * nlh ,
const struct inet_diag_req_v2 * req )
2011-12-09 10:23:51 +04:00
{
2011-12-09 10:24:06 +04:00
int err = - EINVAL ;
2016-04-01 18:52:13 +03:00
struct sock * sk = NULL ;
2011-12-09 10:24:06 +04:00
struct sk_buff * rep ;
2012-07-16 08:28:49 +04:00
struct net * net = sock_net ( in_skb - > sk ) ;
2011-12-09 10:24:06 +04:00
2016-04-01 18:52:13 +03:00
rcu_read_lock ( ) ;
2011-12-09 10:24:06 +04:00
if ( req - > sdiag_family = = AF_INET )
2012-07-16 08:28:49 +04:00
sk = __udp4_lib_lookup ( net ,
2011-12-09 10:24:06 +04:00
req - > id . idiag_src [ 0 ] , req - > id . idiag_sport ,
req - > id . idiag_dst [ 0 ] , req - > id . idiag_dport ,
2017-08-07 18:44:16 +03:00
req - > id . idiag_if , 0 , tbl , NULL ) ;
2011-12-10 03:35:07 +04:00
# if IS_ENABLED(CONFIG_IPV6)
2011-12-09 10:24:06 +04:00
else if ( req - > sdiag_family = = AF_INET6 )
2012-07-16 08:28:49 +04:00
sk = __udp6_lib_lookup ( net ,
2011-12-09 10:24:06 +04:00
( struct in6_addr * ) req - > id . idiag_src ,
req - > id . idiag_sport ,
( struct in6_addr * ) req - > id . idiag_dst ,
req - > id . idiag_dport ,
2016-01-05 01:41:47 +03:00
req - > id . idiag_if , tbl , NULL ) ;
2011-12-10 03:35:07 +04:00
# endif
2017-06-30 13:08:01 +03:00
if ( sk & & ! refcount_inc_not_zero ( & sk - > sk_refcnt ) )
2016-04-01 18:52:13 +03:00
sk = NULL ;
rcu_read_unlock ( ) ;
2011-12-09 10:24:06 +04:00
err = - ENOENT ;
2015-04-03 11:17:26 +03:00
if ( ! sk )
2011-12-09 10:24:06 +04:00
goto out_nosk ;
2011-12-15 06:43:44 +04:00
err = sock_diag_check_cookie ( sk , req - > id . idiag_cookie ) ;
2011-12-09 10:24:06 +04:00
if ( err )
goto out ;
err = - ENOMEM ;
2013-03-27 10:47:04 +04:00
rep = nlmsg_new ( sizeof ( struct inet_diag_msg ) +
sizeof ( struct inet_diag_meminfo ) + 64 ,
GFP_KERNEL ) ;
2011-12-09 10:24:06 +04:00
if ( ! rep )
goto out ;
err = inet_sk_diag_fill ( sk , NULL , rep , req ,
2013-04-17 10:46:57 +04:00
sk_user_ns ( NETLINK_CB ( in_skb ) . sk ) ,
2012-09-08 00:12:54 +04:00
NETLINK_CB ( in_skb ) . portid ,
2016-09-07 18:42:25 +03:00
nlh - > nlmsg_seq , 0 , nlh ,
netlink_net_capable ( in_skb , CAP_NET_ADMIN ) ) ;
2011-12-09 10:24:06 +04:00
if ( err < 0 ) {
WARN_ON ( err = = - EMSGSIZE ) ;
kfree_skb ( rep ) ;
goto out ;
}
2012-09-08 00:12:54 +04:00
err = netlink_unicast ( net - > diag_nlsk , rep , NETLINK_CB ( in_skb ) . portid ,
2011-12-09 10:24:06 +04:00
MSG_DONTWAIT ) ;
if ( err > 0 )
err = 0 ;
out :
if ( sk )
sock_put ( sk ) ;
out_nosk :
return err ;
2011-12-09 10:23:51 +04:00
}
2015-03-10 17:15:54 +03:00
static void udp_dump ( struct udp_table * table , struct sk_buff * skb ,
struct netlink_callback * cb ,
const struct inet_diag_req_v2 * r , struct nlattr * bc )
2011-12-09 10:23:51 +04:00
{
2016-09-07 18:42:25 +03:00
bool net_admin = netlink_net_capable ( cb - > skb , CAP_NET_ADMIN ) ;
2012-07-16 08:28:49 +04:00
struct net * net = sock_net ( skb - > sk ) ;
2016-04-01 18:52:13 +03:00
int num , s_num , slot , s_slot ;
2011-12-09 10:24:21 +04:00
s_slot = cb - > args [ 0 ] ;
num = s_num = cb - > args [ 1 ] ;
2015-01-24 00:02:40 +03:00
for ( slot = s_slot ; slot < = table - > mask ; s_num = 0 , slot + + ) {
2011-12-09 10:24:21 +04:00
struct udp_hslot * hslot = & table - > hash [ slot ] ;
2016-04-01 18:52:13 +03:00
struct sock * sk ;
2011-12-09 10:24:21 +04:00
2015-01-24 00:02:40 +03:00
num = 0 ;
2016-04-01 18:52:13 +03:00
if ( hlist_empty ( & hslot - > head ) )
2011-12-09 10:24:21 +04:00
continue ;
spin_lock_bh ( & hslot - > lock ) ;
2016-04-01 18:52:13 +03:00
sk_for_each ( sk , & hslot - > head ) {
2011-12-09 10:24:21 +04:00
struct inet_sock * inet = inet_sk ( sk ) ;
2012-07-16 08:28:49 +04:00
if ( ! net_eq ( sock_net ( sk ) , net ) )
continue ;
2011-12-09 10:24:21 +04:00
if ( num < s_num )
goto next ;
if ( ! ( r - > idiag_states & ( 1 < < sk - > sk_state ) ) )
goto next ;
if ( r - > sdiag_family ! = AF_UNSPEC & &
sk - > sk_family ! = r - > sdiag_family )
goto next ;
if ( r - > id . idiag_sport ! = inet - > inet_sport & &
r - > id . idiag_sport )
goto next ;
if ( r - > id . idiag_dport ! = inet - > inet_dport & &
r - > id . idiag_dport )
goto next ;
2016-09-07 18:42:25 +03:00
if ( sk_diag_dump ( sk , skb , cb , r , bc , net_admin ) < 0 ) {
2011-12-09 10:24:21 +04:00
spin_unlock_bh ( & hslot - > lock ) ;
goto done ;
}
next :
num + + ;
}
spin_unlock_bh ( & hslot - > lock ) ;
}
done :
cb - > args [ 0 ] = slot ;
cb - > args [ 1 ] = num ;
2011-12-09 10:23:51 +04:00
}
static void udp_diag_dump ( struct sk_buff * skb , struct netlink_callback * cb ,
2015-03-10 17:15:54 +03:00
const struct inet_diag_req_v2 * r , struct nlattr * bc )
2011-12-09 10:23:51 +04:00
{
udp_dump ( & udp_table , skb , cb , r , bc ) ;
}
static int udp_diag_dump_one ( struct sk_buff * in_skb , const struct nlmsghdr * nlh ,
2015-03-10 17:15:54 +03:00
const struct inet_diag_req_v2 * req )
2011-12-09 10:23:51 +04:00
{
return udp_dump_one ( & udp_table , in_skb , nlh , req ) ;
}
2012-04-24 22:15:41 +04:00
static void udp_diag_get_info ( struct sock * sk , struct inet_diag_msg * r ,
void * info )
{
r - > idiag_rqueue = sk_rmem_alloc_get ( sk ) ;
r - > idiag_wqueue = sk_wmem_alloc_get ( sk ) ;
}
2016-08-24 07:06:33 +03:00
# ifdef CONFIG_INET_DIAG_DESTROY
static int __udp_diag_destroy ( struct sk_buff * in_skb ,
const struct inet_diag_req_v2 * req ,
struct udp_table * tbl )
{
struct net * net = sock_net ( in_skb - > sk ) ;
struct sock * sk ;
int err ;
rcu_read_lock ( ) ;
if ( req - > sdiag_family = = AF_INET )
sk = __udp4_lib_lookup ( net ,
req - > id . idiag_dst [ 0 ] , req - > id . idiag_dport ,
req - > id . idiag_src [ 0 ] , req - > id . idiag_sport ,
2017-08-07 18:44:16 +03:00
req - > id . idiag_if , 0 , tbl , NULL ) ;
2016-08-24 07:06:33 +03:00
# if IS_ENABLED(CONFIG_IPV6)
else if ( req - > sdiag_family = = AF_INET6 ) {
if ( ipv6_addr_v4mapped ( ( struct in6_addr * ) req - > id . idiag_dst ) & &
ipv6_addr_v4mapped ( ( struct in6_addr * ) req - > id . idiag_src ) )
sk = __udp4_lib_lookup ( net ,
2016-09-07 07:38:35 +03:00
req - > id . idiag_dst [ 3 ] , req - > id . idiag_dport ,
req - > id . idiag_src [ 3 ] , req - > id . idiag_sport ,
2017-08-07 18:44:16 +03:00
req - > id . idiag_if , 0 , tbl , NULL ) ;
2016-08-24 07:06:33 +03:00
else
sk = __udp6_lib_lookup ( net ,
( 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 , tbl , NULL ) ;
}
# endif
else {
rcu_read_unlock ( ) ;
return - EINVAL ;
}
2017-06-30 13:08:01 +03:00
if ( sk & & ! refcount_inc_not_zero ( & sk - > sk_refcnt ) )
2016-08-24 07:06:33 +03:00
sk = NULL ;
rcu_read_unlock ( ) ;
if ( ! sk )
return - ENOENT ;
if ( sock_diag_check_cookie ( sk , req - > id . idiag_cookie ) ) {
sock_put ( sk ) ;
return - ENOENT ;
}
err = sock_diag_destroy ( sk , ECONNABORTED ) ;
sock_put ( sk ) ;
return err ;
}
static int udp_diag_destroy ( struct sk_buff * in_skb ,
const struct inet_diag_req_v2 * req )
{
return __udp_diag_destroy ( in_skb , req , & udp_table ) ;
}
static int udplite_diag_destroy ( struct sk_buff * in_skb ,
const struct inet_diag_req_v2 * req )
{
return __udp_diag_destroy ( in_skb , req , & udplite_table ) ;
}
# endif
2011-12-09 10:23:51 +04:00
static const struct inet_diag_handler udp_diag_handler = {
. dump = udp_diag_dump ,
. dump_one = udp_diag_dump_one ,
2012-04-24 22:15:41 +04:00
. idiag_get_info = udp_diag_get_info ,
2011-12-09 10:23:51 +04:00
. idiag_type = IPPROTO_UDP ,
2015-06-15 18:26:19 +03:00
. idiag_info_size = 0 ,
2016-08-24 07:06:33 +03:00
# ifdef CONFIG_INET_DIAG_DESTROY
. destroy = udp_diag_destroy ,
# endif
2011-12-09 10:23:51 +04:00
} ;
static void udplite_diag_dump ( struct sk_buff * skb , struct netlink_callback * cb ,
2015-03-10 17:15:54 +03:00
const struct inet_diag_req_v2 * r ,
struct nlattr * bc )
2011-12-09 10:23:51 +04:00
{
udp_dump ( & udplite_table , skb , cb , r , bc ) ;
}
static int udplite_diag_dump_one ( struct sk_buff * in_skb , const struct nlmsghdr * nlh ,
2015-03-10 17:15:54 +03:00
const struct inet_diag_req_v2 * req )
2011-12-09 10:23:51 +04:00
{
return udp_dump_one ( & udplite_table , in_skb , nlh , req ) ;
}
static const struct inet_diag_handler udplite_diag_handler = {
. dump = udplite_diag_dump ,
. dump_one = udplite_diag_dump_one ,
2012-04-24 22:15:41 +04:00
. idiag_get_info = udp_diag_get_info ,
2011-12-09 10:23:51 +04:00
. idiag_type = IPPROTO_UDPLITE ,
2015-06-15 18:26:19 +03:00
. idiag_info_size = 0 ,
2016-08-24 07:06:33 +03:00
# ifdef CONFIG_INET_DIAG_DESTROY
. destroy = udplite_diag_destroy ,
# endif
2011-12-09 10:23:51 +04:00
} ;
static int __init udp_diag_init ( void )
{
int err ;
err = inet_diag_register ( & udp_diag_handler ) ;
if ( err )
goto out ;
err = inet_diag_register ( & udplite_diag_handler ) ;
if ( err )
goto out_lite ;
out :
return err ;
out_lite :
inet_diag_unregister ( & udp_diag_handler ) ;
goto out ;
}
static void __exit udp_diag_exit ( void )
{
inet_diag_unregister ( & udplite_diag_handler ) ;
inet_diag_unregister ( & udp_diag_handler ) ;
}
module_init ( udp_diag_init ) ;
module_exit ( udp_diag_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
2011-12-15 06:43:27 +04:00
MODULE_ALIAS_NET_PF_PROTO_TYPE ( PF_NETLINK , NETLINK_SOCK_DIAG , 2 - 17 /* AF_INET - IPPROTO_UDP */ ) ;
MODULE_ALIAS_NET_PF_PROTO_TYPE ( PF_NETLINK , NETLINK_SOCK_DIAG , 2 - 136 /* AF_INET - IPPROTO_UDPLITE */ ) ;