proc.c 14.3 KB
Newer Older
1
/* SCTP kernel implementation
L
Linus Torvalds 已提交
2 3
 * Copyright (c) 2003 International Business Machines, Corp.
 *
4
 * This file is part of the SCTP kernel implementation
L
Linus Torvalds 已提交
5
 *
6
 * This SCTP implementation is free software;
L
Linus Torvalds 已提交
7 8 9 10 11
 * 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, or (at your option)
 * any later version.
 *
12
 * This SCTP implementation is distributed in the hope that it
L
Linus Torvalds 已提交
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
 *                 ************************
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with GNU CC; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * Please send any bug reports or fixes you make to the
 * email address(es):
 *    lksctp developers <lksctp-developers@lists.sourceforge.net>
 *
 * Or submit a bug report through the following website:
 *    http://www.sf.net/projects/lksctp
 *
 * Written or modified by:
 *    Sridhar Samudrala <sri@us.ibm.com>
 *
 * Any bugs reported given to us we will try to fix... any fixes shared will
 * be incorporated into the next SCTP release.
 */

#include <linux/types.h>
#include <linux/seq_file.h>
#include <linux/init.h>
40
#include <linux/export.h>
L
Linus Torvalds 已提交
41
#include <net/sctp/sctp.h>
42
#include <net/ip.h> /* for snmp_fold_field */
L
Linus Torvalds 已提交
43

A
Alexey Dobriyan 已提交
44
static const struct snmp_mib sctp_snmp_list[] = {
L
Linus Torvalds 已提交
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
	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),
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
	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),
77
	SNMP_MIB_SENTINEL
L
Linus Torvalds 已提交
78 79 80 81 82
};

/* Display sctp snmp mib statistics(/proc/net/sctp/snmp). */
static int sctp_snmp_seq_show(struct seq_file *seq, void *v)
{
83
	struct net *net = seq->private;
L
Linus Torvalds 已提交
84 85 86 87
	int i;

	for (i = 0; sctp_snmp_list[i].name != NULL; i++)
		seq_printf(seq, "%-32s\t%ld\n", sctp_snmp_list[i].name,
88
			   snmp_fold_field((void __percpu **)net->sctp.sctp_statistics,
L
Linus Torvalds 已提交
89 90 91 92 93 94 95 96
				      sctp_snmp_list[i].entry));

	return 0;
}

/* Initialize the seq file operations for 'snmp' object. */
static int sctp_snmp_seq_open(struct inode *inode, struct file *file)
{
97
	return single_open_net(inode, file, sctp_snmp_seq_show);
L
Linus Torvalds 已提交
98 99
}

100
static const struct file_operations sctp_snmp_seq_fops = {
L
Linus Torvalds 已提交
101 102 103 104
	.owner	 = THIS_MODULE,
	.open	 = sctp_snmp_seq_open,
	.read	 = seq_read,
	.llseek	 = seq_lseek,
105
	.release = single_release_net,
L
Linus Torvalds 已提交
106 107 108
};

/* Set up the proc fs entry for 'snmp' object. */
109
int __net_init sctp_snmp_proc_init(struct net *net)
L
Linus Torvalds 已提交
110 111 112
{
	struct proc_dir_entry *p;

113 114
	p = proc_create("snmp", S_IRUGO, net->sctp.proc_net_sctp,
			&sctp_snmp_seq_fops);
L
Linus Torvalds 已提交
115 116 117 118 119 120 121
	if (!p)
		return -ENOMEM;

	return 0;
}

/* Cleanup the proc fs entry for 'snmp' object. */
122
void sctp_snmp_proc_exit(struct net *net)
L
Linus Torvalds 已提交
123
{
124
	remove_proc_entry("snmp", net->sctp.proc_net_sctp);
L
Linus Torvalds 已提交
125 126 127 128 129
}

/* Dump local addresses of an association/endpoint. */
static void sctp_seq_dump_local_addrs(struct seq_file *seq, struct sctp_ep_common *epb)
{
130
	struct sctp_association *asoc;
L
Linus Torvalds 已提交
131
	struct sctp_sockaddr_entry *laddr;
132 133
	struct sctp_transport *peer;
	union sctp_addr *addr, *primary = NULL;
L
Linus Torvalds 已提交
134 135
	struct sctp_af *af;

136 137 138
	if (epb->type == SCTP_EP_TYPE_ASSOCIATION) {
	    asoc = sctp_assoc(epb);
	    peer = asoc->peer.primary_path;
139
	    primary = &peer->saddr;
140 141
	}

142 143 144 145 146
	rcu_read_lock();
	list_for_each_entry_rcu(laddr, &epb->bind_addr.address_list, list) {
		if (!laddr->valid)
			continue;

147
		addr = &laddr->a;
L
Linus Torvalds 已提交
148
		af = sctp_get_af_specific(addr->sa.sa_family);
149 150 151
		if (primary && af->cmp_addr(addr, primary)) {
			seq_printf(seq, "*");
		}
152
		af->seq_dump_addr(seq, addr);
L
Linus Torvalds 已提交
153
	}
154
	rcu_read_unlock();
L
Linus Torvalds 已提交
155 156 157 158 159 160
}

/* 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;
161
	union sctp_addr *addr, *primary;
L
Linus Torvalds 已提交
162 163
	struct sctp_af *af;

164
	primary = &assoc->peer.primary_addr;
165 166
	list_for_each_entry(transport, &assoc->peer.transport_addr_list,
			transports) {
167
		addr = &transport->ipaddr;
L
Linus Torvalds 已提交
168
		af = sctp_get_af_specific(addr->sa.sa_family);
169
		if (af->cmp_addr(addr, primary)) {
170 171
			seq_printf(seq, "*");
		}
172
		af->seq_dump_addr(seq, addr);
L
Linus Torvalds 已提交
173 174 175
	}
}

176 177
static void * sctp_eps_seq_start(struct seq_file *seq, loff_t *pos)
{
178
	if (*pos >= sctp_ep_hashsize)
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
		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)
{
}


static void * sctp_eps_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
197
	if (++*pos >= sctp_ep_hashsize)
198 199 200 201 202 203
		return NULL;

	return pos;
}


L
Linus Torvalds 已提交
204 205 206 207 208 209 210
/* 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_ep_common *epb;
	struct sctp_endpoint *ep;
	struct sock *sk;
211
	struct hlist_node *node;
212
	int    hash = *(loff_t *)v;
213

214
	if (hash >= sctp_ep_hashsize)
215 216
		return -ENOMEM;

217
	head = &sctp_ep_hashtable[hash];
218 219
	sctp_local_bh_disable();
	read_lock(&head->lock);
220
	sctp_for_each_hentry(epb, node, &head->chain) {
221 222
		ep = sctp_ep(epb);
		sk = epb->sk;
223
		if (!net_eq(sock_net(sk), seq_file_net(seq)))
224
			continue;
D
Dan Rosenberg 已提交
225
		seq_printf(seq, "%8pK %8pK %-3d %-3d %-4d %-5d %5d %5lu ", ep, sk,
226
			   sctp_sk(sk)->type, sk->sk_state, hash,
227
			   epb->bind_addr.port,
228 229
			   from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
			   sock_i_ino(sk));
230 231 232

		sctp_seq_dump_local_addrs(seq, epb);
		seq_printf(seq, "\n");
L
Linus Torvalds 已提交
233
	}
234 235
	read_unlock(&head->lock);
	sctp_local_bh_enable();
L
Linus Torvalds 已提交
236 237 238 239

	return 0;
}

240
static const struct seq_operations sctp_eps_ops = {
241 242 243 244 245 246 247
	.start = sctp_eps_seq_start,
	.next  = sctp_eps_seq_next,
	.stop  = sctp_eps_seq_stop,
	.show  = sctp_eps_seq_show,
};


L
Linus Torvalds 已提交
248 249 250
/* Initialize the seq file operations for 'eps' object. */
static int sctp_eps_seq_open(struct inode *inode, struct file *file)
{
251 252
	return seq_open_net(inode, file, &sctp_eps_ops,
			    sizeof(struct seq_net_private));
L
Linus Torvalds 已提交
253 254
}

255
static const struct file_operations sctp_eps_seq_fops = {
L
Linus Torvalds 已提交
256 257 258
	.open	 = sctp_eps_seq_open,
	.read	 = seq_read,
	.llseek	 = seq_lseek,
259
	.release = seq_release_net,
L
Linus Torvalds 已提交
260 261 262
};

/* Set up the proc fs entry for 'eps' object. */
263
int __net_init sctp_eps_proc_init(struct net *net)
L
Linus Torvalds 已提交
264 265 266
{
	struct proc_dir_entry *p;

267 268
	p = proc_create("eps", S_IRUGO, net->sctp.proc_net_sctp,
			&sctp_eps_seq_fops);
L
Linus Torvalds 已提交
269 270 271 272 273 274 275
	if (!p)
		return -ENOMEM;

	return 0;
}

/* Cleanup the proc fs entry for 'eps' object. */
276
void sctp_eps_proc_exit(struct net *net)
L
Linus Torvalds 已提交
277
{
278
	remove_proc_entry("eps", net->sctp.proc_net_sctp);
L
Linus Torvalds 已提交
279 280
}

281 282 283

static void * sctp_assocs_seq_start(struct seq_file *seq, loff_t *pos)
{
284
	if (*pos >= sctp_assoc_hashsize)
285 286 287 288 289 290
		return NULL;

	if (*pos < 0)
		*pos = 0;

	if (*pos == 0)
291 292 293 294
		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\n");
295 296 297 298 299 300 301 302 303 304 305

	return (void *)pos;
}

static void sctp_assocs_seq_stop(struct seq_file *seq, void *v)
{
}


static void * sctp_assocs_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
306
	if (++*pos >= sctp_assoc_hashsize)
307 308 309 310 311
		return NULL;

	return pos;
}

L
Linus Torvalds 已提交
312 313 314 315 316 317 318
/* Display sctp associations (/proc/net/sctp/assocs). */
static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
{
	struct sctp_hashbucket *head;
	struct sctp_ep_common *epb;
	struct sctp_association *assoc;
	struct sock *sk;
319
	struct hlist_node *node;
320
	int    hash = *(loff_t *)v;
321

322
	if (hash >= sctp_assoc_hashsize)
323 324
		return -ENOMEM;

325
	head = &sctp_assoc_hashtable[hash];
326 327
	sctp_local_bh_disable();
	read_lock(&head->lock);
328
	sctp_for_each_hentry(epb, node, &head->chain) {
329 330
		assoc = sctp_assoc(epb);
		sk = epb->sk;
331
		if (!net_eq(sock_net(sk), seq_file_net(seq)))
332
			continue;
333
		seq_printf(seq,
D
Dan Rosenberg 已提交
334
			   "%8pK %8pK %-3d %-3d %-2d %-4d "
335
			   "%4d %8d %8d %7d %5lu %-5d %5d ",
336
			   assoc, sk, sctp_sk(sk)->type, sk->sk_state,
337 338
			   assoc->state, hash,
			   assoc->assoc_id,
339
			   assoc->sndbuf_used,
340
			   atomic_read(&assoc->rmem_alloc),
341 342
			   from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)),
			   sock_i_ino(sk),
343
			   epb->bind_addr.port,
344
			   assoc->peer.port);
345 346 347 348
		seq_printf(seq, " ");
		sctp_seq_dump_local_addrs(seq, epb);
		seq_printf(seq, "<-> ");
		sctp_seq_dump_remote_addrs(seq, assoc);
349 350 351 352 353
		seq_printf(seq, "\t%8lu %5d %5d %4d %4d %4d %8d ",
			assoc->hbinterval, assoc->c.sinit_max_instreams,
			assoc->c.sinit_num_ostreams, assoc->max_retrans,
			assoc->init_retries, assoc->shutdown_retries,
			assoc->rtx_data_chunks);
354
		seq_printf(seq, "\n");
L
Linus Torvalds 已提交
355
	}
356 357
	read_unlock(&head->lock);
	sctp_local_bh_enable();
L
Linus Torvalds 已提交
358 359 360 361

	return 0;
}

362
static const struct seq_operations sctp_assoc_ops = {
363 364 365 366 367 368
	.start = sctp_assocs_seq_start,
	.next  = sctp_assocs_seq_next,
	.stop  = sctp_assocs_seq_stop,
	.show  = sctp_assocs_seq_show,
};

L
Linus Torvalds 已提交
369 370 371
/* Initialize the seq file operations for 'assocs' object. */
static int sctp_assocs_seq_open(struct inode *inode, struct file *file)
{
372 373
	return seq_open_net(inode, file, &sctp_assoc_ops,
			    sizeof(struct seq_net_private));
L
Linus Torvalds 已提交
374 375
}

376
static const struct file_operations sctp_assocs_seq_fops = {
L
Linus Torvalds 已提交
377 378 379
	.open	 = sctp_assocs_seq_open,
	.read	 = seq_read,
	.llseek	 = seq_lseek,
380
	.release = seq_release_net,
L
Linus Torvalds 已提交
381 382 383
};

/* Set up the proc fs entry for 'assocs' object. */
384
int __net_init sctp_assocs_proc_init(struct net *net)
L
Linus Torvalds 已提交
385 386 387
{
	struct proc_dir_entry *p;

388
	p = proc_create("assocs", S_IRUGO, net->sctp.proc_net_sctp,
389
			&sctp_assocs_seq_fops);
L
Linus Torvalds 已提交
390 391 392 393 394 395 396
	if (!p)
		return -ENOMEM;

	return 0;
}

/* Cleanup the proc fs entry for 'assocs' object. */
397
void sctp_assocs_proc_exit(struct net *net)
L
Linus Torvalds 已提交
398
{
399
	remove_proc_entry("assocs", net->sctp.proc_net_sctp);
L
Linus Torvalds 已提交
400
}
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444

static void *sctp_remaddr_seq_start(struct seq_file *seq, loff_t *pos)
{
	if (*pos >= sctp_assoc_hashsize)
		return NULL;

	if (*pos < 0)
		*pos = 0;

	if (*pos == 0)
		seq_printf(seq, "ADDR ASSOC_ID HB_ACT RTO MAX_PATH_RTX "
				"REM_ADDR_RTX  START\n");

	return (void *)pos;
}

static void *sctp_remaddr_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
	if (++*pos >= sctp_assoc_hashsize)
		return NULL;

	return pos;
}

static void sctp_remaddr_seq_stop(struct seq_file *seq, void *v)
{
}

static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
{
	struct sctp_hashbucket *head;
	struct sctp_ep_common *epb;
	struct sctp_association *assoc;
	struct hlist_node *node;
	struct sctp_transport *tsp;
	int    hash = *(loff_t *)v;

	if (hash >= sctp_assoc_hashsize)
		return -ENOMEM;

	head = &sctp_assoc_hashtable[hash];
	sctp_local_bh_disable();
	read_lock(&head->lock);
	sctp_for_each_hentry(epb, node, &head->chain) {
445
		if (!net_eq(sock_net(epb->sk), seq_file_net(seq)))
446
			continue;
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
		assoc = sctp_assoc(epb);
		list_for_each_entry(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_printf(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_printf(seq, "0");

			seq_printf(seq, "\n");
		}
	}

	read_unlock(&head->lock);
	sctp_local_bh_enable();

	return 0;

}

static const struct seq_operations sctp_remaddr_ops = {
	.start = sctp_remaddr_seq_start,
	.next  = sctp_remaddr_seq_next,
	.stop  = sctp_remaddr_seq_stop,
	.show  = sctp_remaddr_seq_show,
};

/* Cleanup the proc fs entry for 'remaddr' object. */
510
void sctp_remaddr_proc_exit(struct net *net)
511
{
512
	remove_proc_entry("remaddr", net->sctp.proc_net_sctp);
513 514 515 516
}

static int sctp_remaddr_seq_open(struct inode *inode, struct file *file)
{
517 518
	return seq_open_net(inode, file, &sctp_remaddr_ops,
			    sizeof(struct seq_net_private));
519 520 521 522 523 524
}

static const struct file_operations sctp_remaddr_seq_fops = {
	.open = sctp_remaddr_seq_open,
	.read = seq_read,
	.llseek = seq_lseek,
525
	.release = seq_release_net,
526 527
};

528
int __net_init sctp_remaddr_proc_init(struct net *net)
529 530 531
{
	struct proc_dir_entry *p;

532 533
	p = proc_create("remaddr", S_IRUGO, net->sctp.proc_net_sctp,
			&sctp_remaddr_seq_fops);
534 535 536 537
	if (!p)
		return -ENOMEM;
	return 0;
}