2019-05-23 12:14:41 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2008-01-11 17:57:09 +03:00
/* SCTP kernel implementation
2005-04-17 02:20:36 +04:00
* Copyright ( c ) 2003 International Business Machines , Corp .
*
2008-01-11 17:57:09 +03:00
* This file is part of the SCTP kernel implementation
2005-04-17 02:20:36 +04:00
*
* Please send any bug reports or fixes you make to the
* email address ( es ) :
2013-07-23 16:51:47 +04:00
* lksctp developers < linux - sctp @ vger . kernel . org >
2005-04-17 02:20:36 +04:00
*
* Written or modified by :
* Sridhar Samudrala < sri @ us . ibm . com >
*/
# include <linux/types.h>
# include <linux/seq_file.h>
# include <linux/init.h>
2011-07-15 19:47:34 +04:00
# include <linux/export.h>
2005-04-17 02:20:36 +04:00
# include <net/sctp/sctp.h>
2008-02-10 10:23:44 +03:00
# include <net/ip.h> /* for snmp_fold_field */
2005-04-17 02:20:36 +04:00
2010-01-22 13:17:26 +03:00
static const struct snmp_mib sctp_snmp_list [ ] = {
2005-04-17 02:20:36 +04:00
SNMP_MIB_ITEM ( " SctpCurrEstab " , SCTP_MIB_CURRESTAB ) ,
SNMP_MIB_ITEM ( " SctpActiveEstabs " , SCTP_MIB_ACTIVEESTABS ) ,
SNMP_MIB_ITEM ( " SctpPassiveEstabs " , SCTP_MIB_PASSIVEESTABS ) ,
SNMP_MIB_ITEM ( " SctpAborteds " , SCTP_MIB_ABORTEDS ) ,
SNMP_MIB_ITEM ( " SctpShutdowns " , SCTP_MIB_SHUTDOWNS ) ,
SNMP_MIB_ITEM ( " SctpOutOfBlues " , SCTP_MIB_OUTOFBLUES ) ,
SNMP_MIB_ITEM ( " SctpChecksumErrors " , SCTP_MIB_CHECKSUMERRORS ) ,
SNMP_MIB_ITEM ( " SctpOutCtrlChunks " , SCTP_MIB_OUTCTRLCHUNKS ) ,
SNMP_MIB_ITEM ( " SctpOutOrderChunks " , SCTP_MIB_OUTORDERCHUNKS ) ,
SNMP_MIB_ITEM ( " SctpOutUnorderChunks " , SCTP_MIB_OUTUNORDERCHUNKS ) ,
SNMP_MIB_ITEM ( " SctpInCtrlChunks " , SCTP_MIB_INCTRLCHUNKS ) ,
SNMP_MIB_ITEM ( " SctpInOrderChunks " , SCTP_MIB_INORDERCHUNKS ) ,
SNMP_MIB_ITEM ( " SctpInUnorderChunks " , SCTP_MIB_INUNORDERCHUNKS ) ,
SNMP_MIB_ITEM ( " SctpFragUsrMsgs " , SCTP_MIB_FRAGUSRMSGS ) ,
SNMP_MIB_ITEM ( " SctpReasmUsrMsgs " , SCTP_MIB_REASMUSRMSGS ) ,
SNMP_MIB_ITEM ( " SctpOutSCTPPacks " , SCTP_MIB_OUTSCTPPACKS ) ,
SNMP_MIB_ITEM ( " SctpInSCTPPacks " , SCTP_MIB_INSCTPPACKS ) ,
2006-08-22 11:15:33 +04:00
SNMP_MIB_ITEM ( " SctpT1InitExpireds " , SCTP_MIB_T1_INIT_EXPIREDS ) ,
SNMP_MIB_ITEM ( " SctpT1CookieExpireds " , SCTP_MIB_T1_COOKIE_EXPIREDS ) ,
SNMP_MIB_ITEM ( " SctpT2ShutdownExpireds " , SCTP_MIB_T2_SHUTDOWN_EXPIREDS ) ,
SNMP_MIB_ITEM ( " SctpT3RtxExpireds " , SCTP_MIB_T3_RTX_EXPIREDS ) ,
SNMP_MIB_ITEM ( " SctpT4RtoExpireds " , SCTP_MIB_T4_RTO_EXPIREDS ) ,
SNMP_MIB_ITEM ( " SctpT5ShutdownGuardExpireds " , SCTP_MIB_T5_SHUTDOWN_GUARD_EXPIREDS ) ,
SNMP_MIB_ITEM ( " SctpDelaySackExpireds " , SCTP_MIB_DELAY_SACK_EXPIREDS ) ,
SNMP_MIB_ITEM ( " SctpAutocloseExpireds " , SCTP_MIB_AUTOCLOSE_EXPIREDS ) ,
SNMP_MIB_ITEM ( " SctpT3Retransmits " , SCTP_MIB_T3_RETRANSMITS ) ,
SNMP_MIB_ITEM ( " SctpPmtudRetransmits " , SCTP_MIB_PMTUD_RETRANSMITS ) ,
SNMP_MIB_ITEM ( " SctpFastRetransmits " , SCTP_MIB_FAST_RETRANSMITS ) ,
SNMP_MIB_ITEM ( " SctpInPktSoftirq " , SCTP_MIB_IN_PKT_SOFTIRQ ) ,
SNMP_MIB_ITEM ( " SctpInPktBacklog " , SCTP_MIB_IN_PKT_BACKLOG ) ,
SNMP_MIB_ITEM ( " SctpInPktDiscards " , SCTP_MIB_IN_PKT_DISCARDS ) ,
SNMP_MIB_ITEM ( " SctpInDataChunkDiscards " , SCTP_MIB_IN_DATA_CHUNK_DISCARDS ) ,
2005-08-23 21:12:04 +04:00
SNMP_MIB_SENTINEL
2005-04-17 02:20:36 +04:00
} ;
/* Display sctp snmp mib statistics(/proc/net/sctp/snmp). */
static int sctp_snmp_seq_show ( struct seq_file * seq , void * v )
{
2016-09-30 06:29:01 +03:00
unsigned long buff [ SCTP_MIB_MAX ] ;
2012-08-06 12:45:15 +04:00
struct net * net = seq - > private ;
2005-04-17 02:20:36 +04:00
int i ;
2016-09-30 06:29:01 +03:00
memset ( buff , 0 , sizeof ( unsigned long ) * SCTP_MIB_MAX ) ;
snmp_get_cpu_field_batch ( buff , sctp_snmp_list ,
net - > sctp . sctp_statistics ) ;
2016-09-30 06:29:04 +03:00
for ( i = 0 ; sctp_snmp_list [ i ] . name ; i + + )
2005-04-17 02:20:36 +04:00
seq_printf ( seq , " %-32s \t %ld \n " , sctp_snmp_list [ i ] . name ,
2016-09-30 06:29:01 +03:00
buff [ i ] ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/* Dump local addresses of an association/endpoint. */
static void sctp_seq_dump_local_addrs ( struct seq_file * seq , struct sctp_ep_common * epb )
{
2005-06-14 02:11:57 +04:00
struct sctp_association * asoc ;
2005-04-17 02:20:36 +04:00
struct sctp_sockaddr_entry * laddr ;
2005-06-14 02:11:57 +04:00
struct sctp_transport * peer ;
union sctp_addr * addr , * primary = NULL ;
2005-04-17 02:20:36 +04:00
struct sctp_af * af ;
2005-06-14 02:11:57 +04:00
if ( epb - > type = = SCTP_EP_TYPE_ASSOCIATION ) {
2013-06-14 20:24:04 +04:00
asoc = sctp_assoc ( epb ) ;
peer = asoc - > peer . primary_path ;
if ( unlikely ( peer = = NULL ) ) {
2013-06-25 20:17:28 +04:00
WARN ( 1 , " Association %p with NULL primary path! \n " , asoc ) ;
2013-06-14 20:24:04 +04:00
return ;
}
primary = & peer - > saddr ;
2005-06-14 02:11:57 +04:00
}
2012-12-06 13:25:04 +04:00
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( laddr , & epb - > bind_addr . address_list , list ) {
if ( ! laddr - > valid )
continue ;
2006-11-21 04:05:23 +03:00
addr = & laddr - > a ;
2005-04-17 02:20:36 +04:00
af = sctp_get_af_specific ( addr - > sa . sa_family ) ;
2005-06-14 02:11:57 +04:00
if ( primary & & af - > cmp_addr ( addr , primary ) ) {
seq_printf ( seq , " * " ) ;
}
2006-11-21 04:05:23 +03:00
af - > seq_dump_addr ( seq , addr ) ;
2005-04-17 02:20:36 +04:00
}
2012-12-06 13:25:04 +04:00
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
}
/* Dump remote addresses of an association. */
static void sctp_seq_dump_remote_addrs ( struct seq_file * seq , struct sctp_association * assoc )
{
struct sctp_transport * transport ;
2005-06-14 02:11:57 +04:00
union sctp_addr * addr , * primary ;
2005-04-17 02:20:36 +04:00
struct sctp_af * af ;
2006-11-21 04:06:04 +03:00
primary = & assoc - > peer . primary_addr ;
2012-12-06 13:25:05 +04:00
list_for_each_entry_rcu ( transport , & assoc - > peer . transport_addr_list ,
2008-04-13 05:54:24 +04:00
transports ) {
2006-11-21 04:05:23 +03:00
addr = & transport - > ipaddr ;
2012-12-06 13:25:05 +04:00
2005-04-17 02:20:36 +04:00
af = sctp_get_af_specific ( addr - > sa . sa_family ) ;
2006-11-21 04:06:04 +03:00
if ( af - > cmp_addr ( addr , primary ) ) {
2005-06-14 02:11:57 +04:00
seq_printf ( seq , " * " ) ;
}
2006-11-21 04:05:23 +03:00
af - > seq_dump_addr ( seq , addr ) ;
2005-04-17 02:20:36 +04:00
}
}
2013-12-23 08:16:51 +04:00
static void * sctp_eps_seq_start ( struct seq_file * seq , loff_t * pos )
2005-06-14 02:11:57 +04:00
{
2006-01-17 22:53:06 +03:00
if ( * pos > = sctp_ep_hashsize )
2005-06-14 02:11:57 +04:00
return NULL ;
if ( * pos < 0 )
* pos = 0 ;
if ( * pos = = 0 )
seq_printf ( seq , " ENDPT SOCK STY SST HBKT LPORT UID INODE LADDRS \n " ) ;
return ( void * ) pos ;
}
static void sctp_eps_seq_stop ( struct seq_file * seq , void * v )
{
}
2013-12-23 08:16:51 +04:00
static void * sctp_eps_seq_next ( struct seq_file * seq , void * v , loff_t * pos )
2005-06-14 02:11:57 +04:00
{
2006-01-17 22:53:06 +03:00
if ( + + * pos > = sctp_ep_hashsize )
2005-06-14 02:11:57 +04:00
return NULL ;
return pos ;
}
2005-04-17 02:20:36 +04:00
/* Display sctp endpoints (/proc/net/sctp/eps). */
static int sctp_eps_seq_show ( struct seq_file * seq , void * v )
{
struct sctp_hashbucket * head ;
struct sctp_endpoint * ep ;
struct sock * sk ;
2006-01-17 22:54:06 +03:00
int hash = * ( loff_t * ) v ;
2005-06-14 02:11:57 +04:00
2006-01-17 22:53:06 +03:00
if ( hash > = sctp_ep_hashsize )
2005-06-14 02:11:57 +04:00
return - ENOMEM ;
2006-01-17 22:53:06 +03:00
head = & sctp_ep_hashtable [ hash ] ;
2017-06-10 10:13:32 +03:00
read_lock_bh ( & head - > lock ) ;
2021-12-22 00:40:30 +03:00
sctp_for_each_hentry ( ep , & head - > chain ) {
sk = ep - > base . sk ;
2012-08-16 05:24:49 +04:00
if ( ! net_eq ( sock_net ( sk ) , seq_file_net ( seq ) ) )
2012-08-06 12:45:15 +04:00
continue ;
2013-08-15 15:42:14 +04:00
seq_printf ( seq , " %8pK %8pK %-3d %-3d %-4d %-5d %5u %5lu " , ep , sk ,
2006-01-17 22:53:06 +03:00
sctp_sk ( sk ) - > type , sk - > sk_state , hash ,
2021-12-22 00:40:30 +03:00
ep - > base . bind_addr . port ,
2012-05-24 11:10:10 +04:00
from_kuid_munged ( seq_user_ns ( seq ) , sock_i_uid ( sk ) ) ,
sock_i_ino ( sk ) ) ;
2005-06-14 02:11:57 +04:00
2021-12-22 00:40:30 +03:00
sctp_seq_dump_local_addrs ( seq , & ep - > base ) ;
2005-06-14 02:11:57 +04:00
seq_printf ( seq , " \n " ) ;
2005-04-17 02:20:36 +04:00
}
2017-06-10 10:13:32 +03:00
read_unlock_bh ( & head - > lock ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-07-11 10:07:31 +04:00
static const struct seq_operations sctp_eps_ops = {
2005-06-14 02:11:57 +04:00
. start = sctp_eps_seq_start ,
. next = sctp_eps_seq_next ,
. stop = sctp_eps_seq_stop ,
. show = sctp_eps_seq_show ,
} ;
2015-12-30 18:50:48 +03:00
struct sctp_ht_iter {
struct seq_net_private p ;
struct rhashtable_iter hti ;
} ;
2005-06-14 02:11:57 +04:00
2016-04-14 10:35:34 +03:00
static void * sctp_transport_seq_start ( struct seq_file * seq , loff_t * pos )
2015-12-30 18:50:48 +03:00
{
2016-04-14 10:35:31 +03:00
struct sctp_ht_iter * iter = seq - > private ;
2015-12-30 18:50:48 +03:00
2017-12-04 21:31:41 +03:00
sctp_transport_walk_start ( & iter - > hti ) ;
2015-12-30 18:50:48 +03:00
2016-04-14 10:35:31 +03:00
return sctp_transport_get_idx ( seq_file_net ( seq ) , & iter - > hti , * pos ) ;
2015-12-30 18:50:48 +03:00
}
2016-04-14 10:35:34 +03:00
static void sctp_transport_seq_stop ( struct seq_file * seq , void * v )
2015-12-30 18:50:48 +03:00
{
2016-04-14 10:35:31 +03:00
struct sctp_ht_iter * iter = seq - > private ;
2021-02-05 03:36:30 +03:00
if ( v & & v ! = SEQ_START_TOKEN ) {
struct sctp_transport * transport = v ;
sctp_transport_put ( transport ) ;
}
2016-04-14 10:35:31 +03:00
sctp_transport_walk_stop ( & iter - > hti ) ;
2015-12-30 18:50:48 +03:00
}
2005-06-14 02:11:57 +04:00
2016-04-14 10:35:34 +03:00
static void * sctp_transport_seq_next ( struct seq_file * seq , void * v , loff_t * pos )
2005-06-14 02:11:57 +04:00
{
2016-04-14 10:35:31 +03:00
struct sctp_ht_iter * iter = seq - > private ;
2021-02-05 03:36:30 +03:00
if ( v & & v ! = SEQ_START_TOKEN ) {
struct sctp_transport * transport = v ;
sctp_transport_put ( transport ) ;
}
2015-12-30 18:50:48 +03:00
+ + * pos ;
2005-06-14 02:11:57 +04:00
2016-04-14 10:35:31 +03:00
return sctp_transport_get_next ( seq_file_net ( seq ) , & iter - > hti ) ;
2005-06-14 02:11:57 +04:00
}
2005-04-17 02:20:36 +04:00
/* Display sctp associations (/proc/net/sctp/assocs). */
static int sctp_assocs_seq_show ( struct seq_file * seq , void * v )
{
2015-12-30 18:50:48 +03:00
struct sctp_transport * transport ;
2005-04-17 02:20:36 +04:00
struct sctp_association * assoc ;
2015-12-30 18:50:48 +03:00
struct sctp_ep_common * epb ;
2005-04-17 02:20:36 +04:00
struct sock * sk ;
2005-06-14 02:11:57 +04:00
2015-12-30 18:50:48 +03:00
if ( v = = SEQ_START_TOKEN ) {
seq_printf ( seq , " ASSOC SOCK STY SST ST HBKT "
" ASSOC-ID TX_QUEUE RX_QUEUE UID INODE LPORT "
" RPORT LADDRS <-> RADDRS "
" HBINT INS OUTS MAXRT T1X T2X RTXC "
" wmema wmemq sndbuf rcvbuf \n " ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2015-12-30 18:50:48 +03:00
transport = ( struct sctp_transport * ) v ;
assoc = transport - > asoc ;
epb = & assoc - > base ;
sk = epb - > sk ;
seq_printf ( seq ,
" %8pK %8pK %-3d %-3d %-2d %-4d "
" %4d %8d %8d %7u %5lu %-5d %5d " ,
assoc , sk , sctp_sk ( sk ) - > type , sk - > sk_state ,
assoc - > state , 0 ,
assoc - > assoc_id ,
assoc - > sndbuf_used ,
atomic_read ( & assoc - > rmem_alloc ) ,
from_kuid_munged ( seq_user_ns ( seq ) , sock_i_uid ( sk ) ) ,
sock_i_ino ( sk ) ,
epb - > bind_addr . port ,
assoc - > peer . port ) ;
seq_printf ( seq , " " ) ;
sctp_seq_dump_local_addrs ( seq , epb ) ;
seq_printf ( seq , " <-> " ) ;
sctp_seq_dump_remote_addrs ( seq , assoc ) ;
seq_printf ( seq , " \t %8lu %5d %5d %4d %4d %4d %8d "
" %8d %8d %8d %8d " ,
2017-05-31 11:36:31 +03:00
assoc - > hbinterval , assoc - > stream . incnt ,
assoc - > stream . outcnt , assoc - > max_retrans ,
2015-12-30 18:50:48 +03:00
assoc - > init_retries , assoc - > shutdown_retries ,
assoc - > rtx_data_chunks ,
2017-06-30 13:08:00 +03:00
refcount_read ( & sk - > sk_wmem_alloc ) ,
2023-08-30 12:45:19 +03:00
READ_ONCE ( sk - > sk_wmem_queued ) ,
2015-12-30 18:50:48 +03:00
sk - > sk_sndbuf ,
sk - > sk_rcvbuf ) ;
seq_printf ( seq , " \n " ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-07-11 10:07:31 +04:00
static const struct seq_operations sctp_assoc_ops = {
2016-04-14 10:35:34 +03:00
. start = sctp_transport_seq_start ,
. next = sctp_transport_seq_next ,
. stop = sctp_transport_seq_stop ,
2005-06-14 02:11:57 +04:00
. show = sctp_assocs_seq_show ,
} ;
2008-05-10 02:14:50 +04:00
static int sctp_remaddr_seq_show ( struct seq_file * seq , void * v )
{
struct sctp_association * assoc ;
2016-02-28 05:33:11 +03:00
struct sctp_transport * transport , * tsp ;
2008-05-10 02:14:50 +04:00
2015-12-30 18:50:48 +03:00
if ( v = = SEQ_START_TOKEN ) {
seq_printf ( seq , " ADDR ASSOC_ID HB_ACT RTO MAX_PATH_RTX "
" REM_ADDR_RTX START STATE \n " ) ;
return 0 ;
}
2008-05-10 02:14:50 +04:00
2016-02-28 05:33:11 +03:00
transport = ( struct sctp_transport * ) v ;
assoc = transport - > asoc ;
2015-12-30 18:50:48 +03:00
list_for_each_entry_rcu ( tsp , & assoc - > peer . transport_addr_list ,
transports ) {
/*
* The remote address ( ADDR )
*/
tsp - > af_specific - > seq_dump_addr ( seq , & tsp - > ipaddr ) ;
seq_printf ( seq , " " ) ;
/*
* The association ID ( ASSOC_ID )
*/
seq_printf ( seq , " %d " , tsp - > asoc - > assoc_id ) ;
/*
* If the Heartbeat is active ( HB_ACT )
* Note : 1 = Active , 0 = Inactive
*/
seq_printf ( seq , " %d " , timer_pending ( & tsp - > hb_timer ) ) ;
/*
* Retransmit time out ( RTO )
*/
seq_printf ( seq , " %lu " , tsp - > rto ) ;
/*
* Maximum path retransmit count ( PATH_MAX_RTX )
*/
seq_printf ( seq , " %d " , tsp - > pathmaxrxt ) ;
/*
* remote address retransmit count ( REM_ADDR_RTX )
* Note : We don ' t have a way to tally this at the moment
* so lets just leave it as zero for the moment
*/
seq_puts ( seq , " 0 " ) ;
/*
* remote address start time ( START ) . This is also not
* currently implemented , but we can record it with a
* jiffies marker in a subsequent patch
*/
seq_puts ( seq , " 0 " ) ;
/*
* The current state of this destination . I . e .
* SCTP_ACTIVE , SCTP_INACTIVE , . . .
*/
seq_printf ( seq , " %d " , tsp - > state ) ;
2012-12-06 13:25:05 +04:00
2015-12-30 18:50:48 +03:00
seq_printf ( seq , " \n " ) ;
2008-05-10 02:14:50 +04:00
}
return 0 ;
}
static const struct seq_operations sctp_remaddr_ops = {
2016-04-14 10:35:34 +03:00
. start = sctp_transport_seq_start ,
. next = sctp_transport_seq_next ,
. stop = sctp_transport_seq_stop ,
2008-05-10 02:14:50 +04:00
. show = sctp_remaddr_seq_show ,
} ;
2018-03-17 02:32:51 +03:00
/* Set up the proc fs entry for the SCTP protocol. */
int __net_init sctp_proc_init ( struct net * net )
2008-05-10 02:14:50 +04:00
{
2018-03-17 02:32:51 +03:00
net - > sctp . proc_net_sctp = proc_net_mkdir ( net , " sctp " , net - > proc_net ) ;
if ( ! net - > sctp . proc_net_sctp )
2008-05-10 02:14:50 +04:00
return - ENOMEM ;
2018-04-13 21:38:35 +03:00
if ( ! proc_create_net_single ( " snmp " , 0444 , net - > sctp . proc_net_sctp ,
sctp_snmp_seq_show , NULL ) )
2018-03-17 02:32:51 +03:00
goto cleanup ;
2018-04-10 20:42:55 +03:00
if ( ! proc_create_net ( " eps " , 0444 , net - > sctp . proc_net_sctp ,
& sctp_eps_ops , sizeof ( struct seq_net_private ) ) )
2018-03-17 02:32:51 +03:00
goto cleanup ;
2018-04-10 20:42:55 +03:00
if ( ! proc_create_net ( " assocs " , 0444 , net - > sctp . proc_net_sctp ,
& sctp_assoc_ops , sizeof ( struct sctp_ht_iter ) ) )
2018-03-17 02:32:51 +03:00
goto cleanup ;
2018-04-10 20:42:55 +03:00
if ( ! proc_create_net ( " remaddr " , 0444 , net - > sctp . proc_net_sctp ,
& sctp_remaddr_ops , sizeof ( struct sctp_ht_iter ) ) )
2018-03-17 02:32:51 +03:00
goto cleanup ;
2008-05-10 02:14:50 +04:00
return 0 ;
2018-03-17 02:32:51 +03:00
cleanup :
remove_proc_subtree ( " sctp " , net - > proc_net ) ;
net - > sctp . proc_net_sctp = NULL ;
return - ENOMEM ;
2008-05-10 02:14:50 +04:00
}