1170461Srrs/*-
2170461Srrs * Copyright (c) 2001-2007, by Weongyo Jeong. All rights reserved.
3224271Stuexen * Copyright (c) 2011, by Michael Tuexen. All rights reserved.
4170461Srrs *
5170461Srrs * Redistribution and use in source and binary forms, with or without
6170461Srrs * modification, are permitted provided that the following conditions are met:
7170461Srrs *
8170461Srrs * a) Redistributions of source code must retain the above copyright notice,
9170461Srrs *   this list of conditions and the following disclaimer.
10170461Srrs *
11170461Srrs * b) Redistributions in binary form must reproduce the above copyright
12170461Srrs *    notice, this list of conditions and the following disclaimer in
13170461Srrs *   the documentation and/or other materials provided with the distribution.
14170461Srrs *
15170461Srrs * c) Neither the name of Cisco Systems, Inc. nor the names of its
16170461Srrs *    contributors may be used to endorse or promote products derived
17170461Srrs *    from this software without specific prior written permission.
18170461Srrs *
19170461Srrs * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20170461Srrs * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21170461Srrs * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22170461Srrs * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23170461Srrs * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24170461Srrs * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25170461Srrs * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26170461Srrs * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27170461Srrs * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28170461Srrs * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29170461Srrs * THE POSSIBILITY OF SUCH DAMAGE.
30170461Srrs */
31170461Srrs
32170461Srrs#if 0
33170461Srrs#ifndef lint
34170461Srrsstatic char sccsid[] = "@(#)sctp.c	0.1 (Berkeley) 4/18/2007";
35170461Srrs#endif /* not lint */
36170461Srrs#endif
37170461Srrs
38170461Srrs#include <sys/cdefs.h>
39170461Srrs__FBSDID("$FreeBSD$");
40170461Srrs
41170461Srrs#include <sys/param.h>
42170461Srrs#include <sys/queue.h>
43170461Srrs#include <sys/types.h>
44170461Srrs#include <sys/socket.h>
45170461Srrs#include <sys/socketvar.h>
46170461Srrs#include <sys/sysctl.h>
47170461Srrs#include <sys/protosw.h>
48170461Srrs
49170461Srrs#include <netinet/in.h>
50170461Srrs#include <netinet/sctp.h>
51170461Srrs#include <netinet/sctp_constants.h>
52170461Srrs#include <arpa/inet.h>
53170461Srrs
54170461Srrs#include <err.h>
55170461Srrs#include <errno.h>
56200462Sdelphij#include <libutil.h>
57200462Sdelphij#include <netdb.h>
58170461Srrs#include <stdint.h>
59170461Srrs#include <stdio.h>
60170461Srrs#include <stdlib.h>
61170461Srrs#include <string.h>
62200462Sdelphij#include <unistd.h>
63170461Srrs#include "netstat.h"
64170461Srrs
65170461Srrs#ifdef SCTP
66170461Srrs
67170461Srrsstatic void sctp_statesprint(uint32_t state);
68170461Srrs
69175061Sobrien#define	NETSTAT_SCTP_STATES_CLOSED		0x0
70175061Sobrien#define	NETSTAT_SCTP_STATES_BOUND		0x1
71175061Sobrien#define	NETSTAT_SCTP_STATES_LISTEN		0x2
72175061Sobrien#define	NETSTAT_SCTP_STATES_COOKIE_WAIT		0x3
73175061Sobrien#define	NETSTAT_SCTP_STATES_COOKIE_ECHOED	0x4
74175061Sobrien#define	NETSTAT_SCTP_STATES_ESTABLISHED		0x5
75175061Sobrien#define	NETSTAT_SCTP_STATES_SHUTDOWN_SENT	0x6
76175061Sobrien#define	NETSTAT_SCTP_STATES_SHUTDOWN_RECEIVED	0x7
77175061Sobrien#define	NETSTAT_SCTP_STATES_SHUTDOWN_ACK_SENT	0x8
78175061Sobrien#define	NETSTAT_SCTP_STATES_SHUTDOWN_PENDING	0x9
79170461Srrs
80246988Scharnierconst char *sctpstates[] = {
81170461Srrs	"CLOSED",
82170461Srrs	"BOUND",
83175061Sobrien	"LISTEN",
84175061Sobrien	"COOKIE_WAIT",
85175061Sobrien	"COOKIE_ECHOED",
86175061Sobrien	"ESTABLISHED",
87170461Srrs	"SHUTDOWN_SENT",
88170461Srrs	"SHUTDOWN_RECEIVED",
89170461Srrs	"SHUTDOWN_ACK_SENT",
90170461Srrs	"SHUTDOWN_PENDING"
91170461Srrs};
92170461Srrs
93170461SrrsLIST_HEAD(xladdr_list, xladdr_entry) xladdr_head;
94170461Srrsstruct xladdr_entry {
95170461Srrs	struct xsctp_laddr *xladdr;
96170461Srrs	LIST_ENTRY(xladdr_entry) xladdr_entries;
97170461Srrs};
98170461Srrs
99170461SrrsLIST_HEAD(xraddr_list, xraddr_entry) xraddr_head;
100170461Srrsstruct xraddr_entry {
101170461Srrs        struct xsctp_raddr *xraddr;
102170461Srrs        LIST_ENTRY(xraddr_entry) xraddr_entries;
103170461Srrs};
104170461Srrs
105224271Stuexen/*
106224271Stuexen * Construct an Internet address representation.
107224271Stuexen * If numeric_addr has been supplied, give
108224271Stuexen * numeric value, otherwise try for symbolic name.
109224271Stuexen */
110238514Stuexen#ifdef INET
111224271Stuexenstatic char *
112224271Stuexeninetname(struct in_addr *inp)
113224271Stuexen{
114224271Stuexen	char *cp;
115224271Stuexen	static char line[MAXHOSTNAMELEN];
116224271Stuexen	struct hostent *hp;
117224271Stuexen	struct netent *np;
118224271Stuexen
119224271Stuexen	cp = 0;
120224271Stuexen	if (!numeric_addr && inp->s_addr != INADDR_ANY) {
121224271Stuexen		int net = inet_netof(*inp);
122224271Stuexen		int lna = inet_lnaof(*inp);
123224271Stuexen
124224271Stuexen		if (lna == INADDR_ANY) {
125224271Stuexen			np = getnetbyaddr(net, AF_INET);
126224271Stuexen			if (np)
127224271Stuexen				cp = np->n_name;
128224271Stuexen		}
129224271Stuexen		if (cp == 0) {
130224271Stuexen			hp = gethostbyaddr((char *)inp, sizeof (*inp), AF_INET);
131224271Stuexen			if (hp) {
132224271Stuexen				cp = hp->h_name;
133224271Stuexen				trimdomain(cp, strlen(cp));
134224271Stuexen			}
135224271Stuexen		}
136224271Stuexen	}
137224271Stuexen	if (inp->s_addr == INADDR_ANY)
138224271Stuexen		strcpy(line, "*");
139224271Stuexen	else if (cp) {
140224271Stuexen		strlcpy(line, cp, sizeof(line));
141224271Stuexen	} else {
142224271Stuexen		inp->s_addr = ntohl(inp->s_addr);
143224271Stuexen#define	C(x)	((u_int)((x) & 0xff))
144224271Stuexen		sprintf(line, "%u.%u.%u.%u", C(inp->s_addr >> 24),
145224271Stuexen		    C(inp->s_addr >> 16), C(inp->s_addr >> 8), C(inp->s_addr));
146224271Stuexen		inp->s_addr = htonl(inp->s_addr);
147224271Stuexen	}
148224271Stuexen	return (line);
149224271Stuexen}
150238514Stuexen#endif
151224271Stuexen
152224271Stuexen#ifdef INET6
153224271Stuexenstatic char ntop_buf[INET6_ADDRSTRLEN];
154224271Stuexen
155224271Stuexenstatic char *
156224271Stuexeninet6name(struct in6_addr *in6p)
157224271Stuexen{
158224271Stuexen	char *cp;
159224271Stuexen	static char line[50];
160224271Stuexen	struct hostent *hp;
161224271Stuexen	static char domain[MAXHOSTNAMELEN];
162224271Stuexen	static int first = 1;
163224271Stuexen
164224271Stuexen	if (first && !numeric_addr) {
165224271Stuexen		first = 0;
166224271Stuexen		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
167229403Sed		    (cp = strchr(domain, '.')))
168224271Stuexen			(void) strcpy(domain, cp + 1);
169224271Stuexen		else
170224271Stuexen			domain[0] = 0;
171224271Stuexen	}
172224271Stuexen	cp = 0;
173224271Stuexen	if (!numeric_addr && !IN6_IS_ADDR_UNSPECIFIED(in6p)) {
174224271Stuexen		hp = gethostbyaddr((char *)in6p, sizeof(*in6p), AF_INET6);
175224271Stuexen		if (hp) {
176229403Sed			if ((cp = strchr(hp->h_name, '.')) &&
177224271Stuexen			    !strcmp(cp + 1, domain))
178224271Stuexen				*cp = 0;
179224271Stuexen			cp = hp->h_name;
180224271Stuexen		}
181224271Stuexen	}
182224271Stuexen	if (IN6_IS_ADDR_UNSPECIFIED(in6p))
183224271Stuexen		strcpy(line, "*");
184224271Stuexen	else if (cp)
185224271Stuexen		strcpy(line, cp);
186224271Stuexen	else
187224271Stuexen		sprintf(line, "%s",
188224271Stuexen			inet_ntop(AF_INET6, (void *)in6p, ntop_buf,
189224271Stuexen				sizeof(ntop_buf)));
190224271Stuexen	return (line);
191224271Stuexen}
192224271Stuexen#endif
193224271Stuexen
194224271Stuexenstatic void
195224271Stuexensctp_print_address(union sctp_sockstore *address, int port, int num_port)
196224271Stuexen{
197224271Stuexen	struct servent *sp = 0;
198224271Stuexen	char line[80], *cp;
199224271Stuexen	int width;
200224271Stuexen
201224271Stuexen	switch (address->sa.sa_family) {
202238514Stuexen#ifdef INET
203224271Stuexen	case AF_INET:
204224271Stuexen		sprintf(line, "%.*s.", Wflag ? 39 : 16, inetname(&address->sin.sin_addr));
205224271Stuexen		break;
206238514Stuexen#endif
207224271Stuexen#ifdef INET6
208224271Stuexen	case AF_INET6:
209224271Stuexen		sprintf(line, "%.*s.", Wflag ? 39 : 16, inet6name(&address->sin6.sin6_addr));
210224271Stuexen		break;
211224271Stuexen#endif
212224271Stuexen	default:
213224271Stuexen		sprintf(line, "%.*s.", Wflag ? 39 : 16, "");
214224271Stuexen		break;
215224271Stuexen	}
216229403Sed	cp = strchr(line, '\0');
217224271Stuexen	if (!num_port && port)
218224271Stuexen		sp = getservbyport((int)port, "sctp");
219224271Stuexen	if (sp || port == 0)
220224271Stuexen		sprintf(cp, "%.15s ", sp ? sp->s_name : "*");
221224271Stuexen	else
222224271Stuexen		sprintf(cp, "%d ", ntohs((u_short)port));
223224271Stuexen	width = Wflag ? 45 : 22;
224224271Stuexen	printf("%-*.*s ", width, width, line);
225224271Stuexen}
226224271Stuexen
227170461Srrsstatic int
228170461Srrssctp_skip_xinpcb_ifneed(char *buf, const size_t buflen, size_t *offset)
229170461Srrs{
230170461Srrs	int exist_tcb = 0;
231170461Srrs	struct xsctp_tcb *xstcb;
232170461Srrs	struct xsctp_raddr *xraddr;
233170461Srrs	struct xsctp_laddr *xladdr;
234170461Srrs
235170461Srrs	while (*offset < buflen) {
236170461Srrs		xladdr = (struct xsctp_laddr *)(buf + *offset);
237170461Srrs		*offset += sizeof(struct xsctp_laddr);
238170461Srrs		if (xladdr->last == 1)
239170461Srrs			break;
240170461Srrs	}
241175061Sobrien
242170461Srrs	while (*offset < buflen) {
243170461Srrs		xstcb = (struct xsctp_tcb *)(buf + *offset);
244170461Srrs		*offset += sizeof(struct xsctp_tcb);
245170461Srrs		if (xstcb->last == 1)
246170461Srrs			break;
247170461Srrs
248170461Srrs		exist_tcb = 1;
249170461Srrs
250170461Srrs		while (*offset < buflen) {
251170461Srrs			xladdr = (struct xsctp_laddr *)(buf + *offset);
252170461Srrs			*offset += sizeof(struct xsctp_laddr);
253170461Srrs			if (xladdr->last == 1)
254170461Srrs				break;
255170461Srrs		}
256170461Srrs
257170461Srrs		while (*offset < buflen) {
258170461Srrs			xraddr = (struct xsctp_raddr *)(buf + *offset);
259170461Srrs			*offset += sizeof(struct xsctp_raddr);
260170461Srrs			if (xraddr->last == 1)
261170461Srrs				break;
262170461Srrs		}
263170461Srrs	}
264170461Srrs
265170461Srrs	/*
266170461Srrs	 * If Lflag is set, we don't care about the return value.
267170461Srrs	 */
268170461Srrs	if (Lflag)
269170461Srrs		return 0;
270170461Srrs
271170461Srrs	return exist_tcb;
272170461Srrs}
273170461Srrs
274170461Srrsstatic void
275224271Stuexensctp_process_tcb(struct xsctp_tcb *xstcb,
276170461Srrs    char *buf, const size_t buflen, size_t *offset, int *indent)
277170461Srrs{
278170461Srrs	int i, xl_total = 0, xr_total = 0, x_max;
279170461Srrs	struct xsctp_raddr *xraddr;
280170461Srrs	struct xsctp_laddr *xladdr;
281170461Srrs	struct xladdr_entry *prev_xl = NULL, *xl = NULL, *xl_tmp;
282170461Srrs	struct xraddr_entry *prev_xr = NULL, *xr = NULL, *xr_tmp;
283170461Srrs
284170461Srrs	LIST_INIT(&xladdr_head);
285170461Srrs	LIST_INIT(&xraddr_head);
286170461Srrs
287170461Srrs	/*
288170461Srrs	 * Make `struct xladdr_list' list and `struct xraddr_list' list
289170461Srrs	 * to handle the address flexibly.
290170461Srrs	 */
291170461Srrs	while (*offset < buflen) {
292170461Srrs		xladdr = (struct xsctp_laddr *)(buf + *offset);
293170461Srrs		*offset += sizeof(struct xsctp_laddr);
294170461Srrs		if (xladdr->last == 1)
295170461Srrs			break;
296175061Sobrien
297170461Srrs		prev_xl = xl;
298170461Srrs		xl = malloc(sizeof(struct xladdr_entry));
299170461Srrs		if (xl == NULL) {
300175061Sobrien			warnx("malloc %lu bytes",
301170461Srrs			    (u_long)sizeof(struct xladdr_entry));
302170461Srrs			goto out;
303170461Srrs		}
304170461Srrs		xl->xladdr = xladdr;
305170461Srrs		if (prev_xl == NULL)
306170461Srrs			LIST_INSERT_HEAD(&xladdr_head, xl, xladdr_entries);
307170461Srrs		else
308170461Srrs			LIST_INSERT_AFTER(prev_xl, xl, xladdr_entries);
309170461Srrs		xl_total++;
310170461Srrs	}
311175061Sobrien
312170461Srrs	while (*offset < buflen) {
313170461Srrs		xraddr = (struct xsctp_raddr *)(buf + *offset);
314170461Srrs		*offset += sizeof(struct xsctp_raddr);
315170461Srrs		if (xraddr->last == 1)
316170461Srrs			break;
317175061Sobrien
318170461Srrs		prev_xr = xr;
319170461Srrs		xr = malloc(sizeof(struct xraddr_entry));
320170461Srrs		if (xr == NULL) {
321175061Sobrien			warnx("malloc %lu bytes",
322170461Srrs			    (u_long)sizeof(struct xraddr_entry));
323170461Srrs			goto out;
324170461Srrs		}
325170461Srrs		xr->xraddr = xraddr;
326170461Srrs		if (prev_xr == NULL)
327170461Srrs			LIST_INSERT_HEAD(&xraddr_head, xr, xraddr_entries);
328170461Srrs		else
329170461Srrs			LIST_INSERT_AFTER(prev_xr, xr, xraddr_entries);
330170461Srrs		xr_total++;
331170461Srrs	}
332175061Sobrien
333170461Srrs	/*
334170461Srrs	 * Let's print the address infos.
335170461Srrs	 */
336170461Srrs	xl = LIST_FIRST(&xladdr_head);
337170461Srrs	xr = LIST_FIRST(&xraddr_head);
338170461Srrs	x_max = (xl_total > xr_total) ? xl_total : xr_total;
339170461Srrs	for (i = 0; i < x_max; i++) {
340170461Srrs		if (((*indent == 0) && i > 0) || *indent > 0)
341224271Stuexen			printf("%-12s ", " ");
342175061Sobrien
343170461Srrs		if (xl != NULL) {
344224271Stuexen			sctp_print_address(&(xl->xladdr->address),
345224271Stuexen			    htons(xstcb->local_port), numeric_port);
346224271Stuexen		} else {
347224271Stuexen			if (Wflag) {
348224271Stuexen				printf("%-45s ", " ");
349224271Stuexen			} else {
350224271Stuexen				printf("%-22s ", " ");
351170461Srrs			}
352170461Srrs		}
353175061Sobrien
354170461Srrs		if (xr != NULL && !Lflag) {
355224271Stuexen			sctp_print_address(&(xr->xraddr->address),
356224271Stuexen			    htons(xstcb->remote_port), numeric_port);
357170461Srrs		}
358175061Sobrien
359170461Srrs		if (xl != NULL)
360170461Srrs			xl = LIST_NEXT(xl, xladdr_entries);
361170461Srrs		if (xr != NULL)
362170461Srrs			xr = LIST_NEXT(xr, xraddr_entries);
363175061Sobrien
364170461Srrs		if (i == 0 && !Lflag)
365170461Srrs			sctp_statesprint(xstcb->state);
366175061Sobrien
367170461Srrs		if (i < x_max)
368170461Srrs			putchar('\n');
369170461Srrs	}
370175061Sobrien
371170461Srrsout:
372170461Srrs	/*
373170461Srrs	 * Free the list which be used to handle the address.
374170461Srrs	 */
375170461Srrs	xl = LIST_FIRST(&xladdr_head);
376170461Srrs	while (xl != NULL) {
377170461Srrs		xl_tmp = LIST_NEXT(xl, xladdr_entries);
378170461Srrs		free(xl);
379170461Srrs		xl = xl_tmp;
380170461Srrs	}
381175061Sobrien
382170461Srrs	xr = LIST_FIRST(&xraddr_head);
383170461Srrs	while (xr != NULL) {
384170461Srrs		xr_tmp = LIST_NEXT(xr, xraddr_entries);
385170461Srrs		free(xr);
386170461Srrs		xr = xr_tmp;
387170461Srrs	}
388170461Srrs}
389170461Srrs
390170461Srrsstatic void
391224271Stuexensctp_process_inpcb(struct xsctp_inpcb *xinpcb,
392170461Srrs    char *buf, const size_t buflen, size_t *offset)
393170461Srrs{
394224271Stuexen	int indent = 0, xladdr_total = 0, is_listening = 0;
395170461Srrs	static int first = 1;
396246988Scharnier	const char *tname, *pname;
397170461Srrs	struct xsctp_tcb *xstcb;
398170461Srrs	struct xsctp_laddr *xladdr;
399224271Stuexen	size_t offset_laddr;
400224271Stuexen	int process_closed;
401170461Srrs
402224271Stuexen	if (xinpcb->maxqlen > 0)
403170461Srrs		is_listening = 1;
404170461Srrs
405170461Srrs	if (first) {
406170461Srrs		if (!Lflag) {
407170461Srrs			printf("Active SCTP associations");
408170461Srrs			if (aflag)
409170461Srrs				printf(" (including servers)");
410170461Srrs		} else
411170461Srrs			printf("Current listen queue sizes (qlen/maxqlen)");
412170461Srrs		putchar('\n');
413170461Srrs		if (Lflag)
414224271Stuexen			printf("%-6.6s %-5.5s %-8.8s %-22.22s\n",
415170461Srrs			    "Proto", "Type", "Listen", "Local Address");
416170461Srrs		else
417224271Stuexen			if (Wflag)
418224271Stuexen				printf("%-6.6s %-5.5s %-45.45s %-45.45s %s\n",
419224271Stuexen				    "Proto", "Type",
420224271Stuexen				    "Local Address", "Foreign Address",
421224271Stuexen				    "(state)");
422224271Stuexen			else
423224271Stuexen				printf("%-6.6s %-5.5s %-22.22s %-22.22s %s\n",
424224271Stuexen				    "Proto", "Type",
425224271Stuexen				    "Local Address", "Foreign Address",
426224271Stuexen				    "(state)");
427170461Srrs		first = 0;
428170461Srrs	}
429224271Stuexen	xladdr = (struct xsctp_laddr *)(buf + *offset);
430224271Stuexen	if (Lflag && !is_listening) {
431213620Sdim		sctp_skip_xinpcb_ifneed(buf, buflen, offset);
432170461Srrs		return;
433170461Srrs	}
434170461Srrs
435224271Stuexen	if (xinpcb->flags & SCTP_PCB_FLAGS_BOUND_V6) {
436224271Stuexen		/* Can't distinguish between sctp46 and sctp6 */
437224271Stuexen		pname = "sctp46";
438224271Stuexen	} else {
439224271Stuexen		pname = "sctp4";
440224271Stuexen	}
441175061Sobrien
442170461Srrs	if (xinpcb->flags & SCTP_PCB_FLAGS_TCPTYPE)
443170461Srrs		tname = "1to1";
444170461Srrs	else if (xinpcb->flags & SCTP_PCB_FLAGS_UDPTYPE)
445170461Srrs		tname = "1toN";
446170461Srrs	else
447224271Stuexen		tname = "????";
448175061Sobrien
449170461Srrs	if (Lflag) {
450170461Srrs		char buf1[9];
451175061Sobrien
452170461Srrs		snprintf(buf1, 9, "%hu/%hu", xinpcb->qlen, xinpcb->maxqlen);
453224271Stuexen		printf("%-6.6s %-5.5s ", pname, tname);
454170461Srrs		printf("%-8.8s ", buf1);
455170461Srrs	}
456224271Stuexen
457224271Stuexen	offset_laddr = *offset;
458224271Stuexen	process_closed = 0;
459224271Stuexenretry:
460170461Srrs	while (*offset < buflen) {
461170461Srrs		xladdr = (struct xsctp_laddr *)(buf + *offset);
462170461Srrs		*offset += sizeof(struct xsctp_laddr);
463224271Stuexen		if (xladdr->last) {
464224271Stuexen			if (aflag && !Lflag && (xladdr_total == 0) && process_closed) {
465224271Stuexen				printf("%-6.6s %-5.5s ", pname, tname);
466224271Stuexen				if (Wflag) {
467224271Stuexen					printf("%-91.91s CLOSED", " ");
468224271Stuexen				} else {
469224271Stuexen					printf("%-45.45s CLOSED", " ");
470224271Stuexen				}
471224271Stuexen			}
472224271Stuexen			if (process_closed || is_listening) {
473224271Stuexen				putchar('\n');
474224271Stuexen			}
475170461Srrs			break;
476224271Stuexen		}
477170461Srrs
478224271Stuexen		if (!Lflag && !is_listening && !process_closed)
479170461Srrs			continue;
480170461Srrs
481224271Stuexen		if (xladdr_total == 0) {
482224271Stuexen			printf("%-6.6s %-5.5s ", pname, tname);
483224271Stuexen		} else {
484170461Srrs			putchar('\n');
485170461Srrs			printf((Lflag) ?
486224271Stuexen			    "%-21.21s " : "%-12.12s ", " ");
487170461Srrs		}
488224271Stuexen		sctp_print_address(&(xladdr->address),
489224271Stuexen		    htons(xinpcb->local_port), numeric_port);
490224271Stuexen		if (aflag && !Lflag && xladdr_total == 0) {
491224271Stuexen			if (Wflag) {
492224271Stuexen				if (process_closed) {
493224271Stuexen					printf("%-45.45s CLOSED", " ");
494224271Stuexen				} else {
495224271Stuexen					printf("%-45.45s LISTEN", " ");
496224271Stuexen				}
497224271Stuexen			} else {
498224271Stuexen				if (process_closed) {
499224271Stuexen					printf("%-22.22s CLOSED", " ");
500224271Stuexen				} else {
501224271Stuexen					printf("%-22.22s LISTEN", " ");
502224271Stuexen				}
503224271Stuexen			}
504224271Stuexen		}
505170461Srrs		xladdr_total++;
506170461Srrs	}
507170461Srrs
508170461Srrs	xstcb = (struct xsctp_tcb *)(buf + *offset);
509170461Srrs	*offset += sizeof(struct xsctp_tcb);
510224271Stuexen	if (aflag && (xladdr_total == 0) && xstcb->last && !process_closed) {
511224271Stuexen		process_closed = 1;
512224271Stuexen		*offset = offset_laddr;
513224271Stuexen		goto retry;
514224271Stuexen	}
515170461Srrs	while (xstcb->last == 0 && *offset < buflen) {
516224271Stuexen		printf("%-6.6s %-5.5s ", pname, tname);
517224271Stuexen		sctp_process_tcb(xstcb, buf, buflen, offset, &indent);
518170461Srrs		indent++;
519170461Srrs		xstcb = (struct xsctp_tcb *)(buf + *offset);
520170461Srrs		*offset += sizeof(struct xsctp_tcb);
521170461Srrs	}
522170461Srrs}
523170461Srrs
524170461Srrs/*
525170461Srrs * Print a summary of SCTP connections related to an Internet
526170461Srrs * protocol.
527170461Srrs */
528170461Srrsvoid
529171465Sjhbsctp_protopr(u_long off __unused,
530246988Scharnier    const char *name __unused, int af1 __unused, int proto)
531170461Srrs{
532170461Srrs	char *buf;
533170461Srrs	const char *mibvar = "net.inet.sctp.assoclist";
534170646Sdelphij	size_t offset = 0;
535170461Srrs	size_t len = 0;
536170461Srrs	struct xsctp_inpcb *xinpcb;
537175061Sobrien
538170461Srrs	if (proto != IPPROTO_SCTP)
539170461Srrs		return;
540170461Srrs
541170461Srrs	if (sysctlbyname(mibvar, 0, &len, 0, 0) < 0) {
542170461Srrs		if (errno != ENOENT)
543170461Srrs			warn("sysctl: %s", mibvar);
544170461Srrs		return;
545170461Srrs	}
546170461Srrs	if ((buf = malloc(len)) == 0) {
547170461Srrs		warnx("malloc %lu bytes", (u_long)len);
548170461Srrs		return;
549170461Srrs	}
550170461Srrs	if (sysctlbyname(mibvar, buf, &len, 0, 0) < 0) {
551170461Srrs		warn("sysctl: %s", mibvar);
552170461Srrs		free(buf);
553170461Srrs		return;
554170461Srrs	}
555170461Srrs
556170461Srrs	xinpcb = (struct xsctp_inpcb *)(buf + offset);
557170461Srrs	offset += sizeof(struct xsctp_inpcb);
558170461Srrs	while (xinpcb->last == 0 && offset < len) {
559224271Stuexen		sctp_process_inpcb(xinpcb, buf, (const size_t)len,
560170461Srrs		    &offset);
561170461Srrs
562170461Srrs		xinpcb = (struct xsctp_inpcb *)(buf + offset);
563170461Srrs		offset += sizeof(struct xsctp_inpcb);
564170461Srrs	}
565170461Srrs
566170461Srrs	free(buf);
567170461Srrs}
568170461Srrs
569170461Srrsstatic void
570170461Srrssctp_statesprint(uint32_t state)
571170461Srrs{
572170461Srrs	int idx;
573170461Srrs
574170461Srrs	switch (state) {
575170461Srrs	case SCTP_STATE_COOKIE_WAIT:
576170461Srrs		idx = NETSTAT_SCTP_STATES_COOKIE_WAIT;
577170461Srrs		break;
578170461Srrs	case SCTP_STATE_COOKIE_ECHOED:
579170461Srrs		idx = NETSTAT_SCTP_STATES_COOKIE_ECHOED;
580170461Srrs		break;
581170461Srrs	case SCTP_STATE_OPEN:
582170461Srrs		idx = NETSTAT_SCTP_STATES_ESTABLISHED;
583170461Srrs		break;
584170461Srrs	case SCTP_STATE_SHUTDOWN_SENT:
585170461Srrs		idx = NETSTAT_SCTP_STATES_SHUTDOWN_SENT;
586170461Srrs		break;
587170461Srrs	case SCTP_STATE_SHUTDOWN_RECEIVED:
588170461Srrs		idx = NETSTAT_SCTP_STATES_SHUTDOWN_RECEIVED;
589170461Srrs		break;
590170461Srrs	case SCTP_STATE_SHUTDOWN_ACK_SENT:
591170461Srrs		idx = NETSTAT_SCTP_STATES_SHUTDOWN_ACK_SENT;
592170461Srrs		break;
593170461Srrs	case SCTP_STATE_SHUTDOWN_PENDING:
594170461Srrs		idx = NETSTAT_SCTP_STATES_SHUTDOWN_PENDING;
595170461Srrs		break;
596170461Srrs	default:
597170461Srrs		printf("UNKNOWN 0x%08x", state);
598170461Srrs		return;
599170461Srrs	}
600170461Srrs
601170461Srrs	printf("%s", sctpstates[idx]);
602170461Srrs}
603170461Srrs
604170461Srrs/*
605170461Srrs * Dump SCTP statistics structure.
606170461Srrs */
607170461Srrsvoid
608171465Sjhbsctp_stats(u_long off, const char *name, int af1 __unused, int proto __unused)
609170461Srrs{
610170461Srrs	struct sctpstat sctpstat, zerostat;
611170461Srrs	size_t len = sizeof(sctpstat);
612170461Srrs
613171465Sjhb	if (live) {
614171465Sjhb		if (zflag)
615171465Sjhb			memset(&zerostat, 0, len);
616171465Sjhb		if (sysctlbyname("net.inet.sctp.stats", &sctpstat, &len,
617171465Sjhb		    zflag ? &zerostat : NULL, zflag ? len : 0) < 0) {
618230555Stuexen			if (errno != ENOENT)
619230555Stuexen				warn("sysctl: net.inet.sctp.stats");
620171465Sjhb			return;
621171465Sjhb		}
622171465Sjhb	} else
623171465Sjhb		kread(off, &sctpstat, len);
624170461Srrs
625170461Srrs	printf ("%s:\n", name);
626170461Srrs
627170461Srrs#define	p(f, m) if (sctpstat.f || sflag <= 1) \
628172103Srrs    printf(m, (uintmax_t)sctpstat.f, plural(sctpstat.f))
629170461Srrs#define	p1a(f, m) if (sctpstat.f || sflag <= 1) \
630172103Srrs    printf(m, (uintmax_t)sctpstat.f)
631170461Srrs
632170461Srrs	/*
633170461Srrs	 * input statistics
634170461Srrs	 */
635172103Srrs	p(sctps_recvpackets, "\t%ju input packet%s\n");
636172103Srrs	p(sctps_recvdatagrams, "\t\t%ju datagram%s\n");
637172103Srrs	p(sctps_recvpktwithdata, "\t\t%ju packet%s that had data\n");
638172103Srrs	p(sctps_recvsacks, "\t\t%ju input SACK chunk%s\n");
639172103Srrs	p(sctps_recvdata, "\t\t%ju input DATA chunk%s\n");
640172103Srrs	p(sctps_recvdupdata, "\t\t%ju duplicate DATA chunk%s\n");
641172103Srrs	p(sctps_recvheartbeat, "\t\t%ju input HB chunk%s\n");
642172103Srrs	p(sctps_recvheartbeatack, "\t\t%ju HB-ACK chunk%s\n");
643172103Srrs	p(sctps_recvecne, "\t\t%ju input ECNE chunk%s\n");
644172103Srrs	p(sctps_recvauth, "\t\t%ju input AUTH chunk%s\n");
645172103Srrs	p(sctps_recvauthmissing, "\t\t%ju chunk%s missing AUTH\n");
646172103Srrs	p(sctps_recvivalhmacid, "\t\t%ju invalid HMAC id%s received\n");
647172720Srrs	p(sctps_recvivalkeyid, "\t\t%ju invalid secret id%s received\n");
648172103Srrs	p1a(sctps_recvauthfailed, "\t\t%ju auth failed\n");
649172720Srrs	p1a(sctps_recvexpress, "\t\t%ju fast path receives all one chunk\n");
650172720Srrs	p1a(sctps_recvexpressm, "\t\t%ju fast path multi-part data\n");
651170461Srrs
652170461Srrs	/*
653170461Srrs	 * output statistics
654170461Srrs	 */
655172103Srrs	p(sctps_sendpackets, "\t%ju output packet%s\n");
656172103Srrs	p(sctps_sendsacks, "\t\t%ju output SACK%s\n");
657172103Srrs	p(sctps_senddata, "\t\t%ju output DATA chunk%s\n");
658172720Srrs	p(sctps_sendretransdata, "\t\t%ju retransmitted DATA chunk%s\n");
659172720Srrs	p(sctps_sendfastretrans, "\t\t%ju fast retransmitted DATA chunk%s\n");
660172103Srrs	p(sctps_sendmultfastretrans, "\t\t%ju FR'%s that happened more "
661178252Srrs	    "than once to same chunk\n");
662219613Sbrucec	p(sctps_sendheartbeat, "\t\t%ju output HB chunk%s\n");
663172103Srrs	p(sctps_sendecne, "\t\t%ju output ECNE chunk%s\n");
664172103Srrs	p(sctps_sendauth, "\t\t%ju output AUTH chunk%s\n");
665172103Srrs	p1a(sctps_senderrors, "\t\t%ju ip_output error counter\n");
666170461Srrs
667170461Srrs	/*
668170461Srrs	 * PCKDROPREP statistics
669170461Srrs	 */
670170882Srrs	printf("\tPacket drop statistics:\n");
671172103Srrs	p1a(sctps_pdrpfmbox, "\t\t%ju from middle box\n");
672172720Srrs	p1a(sctps_pdrpfehos, "\t\t%ju from end host\n");
673172103Srrs	p1a(sctps_pdrpmbda, "\t\t%ju with data\n");
674172103Srrs	p1a(sctps_pdrpmbct, "\t\t%ju non-data, non-endhost\n");
675172720Srrs	p1a(sctps_pdrpbwrpt, "\t\t%ju non-endhost, bandwidth rep only\n");
676172103Srrs	p1a(sctps_pdrpcrupt, "\t\t%ju not enough for chunk header\n");
677172103Srrs	p1a(sctps_pdrpnedat, "\t\t%ju not enough data to confirm\n");
678172720Srrs	p1a(sctps_pdrppdbrk, "\t\t%ju where process_chunk_drop said break\n");
679172103Srrs	p1a(sctps_pdrptsnnf, "\t\t%ju failed to find TSN\n");
680172720Srrs	p1a(sctps_pdrpdnfnd, "\t\t%ju attempt reverse TSN lookup\n");
681172720Srrs	p1a(sctps_pdrpdiwnp, "\t\t%ju e-host confirms zero-rwnd\n");
682172720Srrs	p1a(sctps_pdrpdizrw, "\t\t%ju midbox confirms no space\n");
683172103Srrs	p1a(sctps_pdrpbadd, "\t\t%ju data did not match TSN\n");
684172103Srrs	p(sctps_pdrpmark, "\t\t%ju TSN'%s marked for Fast Retran\n");
685170461Srrs
686170461Srrs	/*
687170461Srrs	 * Timeouts
688170461Srrs	 */
689170882Srrs	printf("\tTimeouts:\n");
690172103Srrs	p(sctps_timoiterator, "\t\t%ju iterator timer%s fired\n");
691172103Srrs	p(sctps_timodata, "\t\t%ju T3 data time out%s\n");
692172103Srrs	p(sctps_timowindowprobe, "\t\t%ju window probe (T3) timer%s fired\n");
693172103Srrs	p(sctps_timoinit, "\t\t%ju INIT timer%s fired\n");
694172720Srrs	p(sctps_timosack, "\t\t%ju sack timer%s fired\n");
695172720Srrs	p(sctps_timoshutdown, "\t\t%ju shutdown timer%s fired\n");
696172103Srrs	p(sctps_timoheartbeat, "\t\t%ju heartbeat timer%s fired\n");
697172103Srrs	p1a(sctps_timocookie, "\t\t%ju a cookie timeout fired\n");
698172103Srrs	p1a(sctps_timosecret, "\t\t%ju an endpoint changed its cookie"
699170882Srrs	    "secret\n");
700172103Srrs	p(sctps_timopathmtu, "\t\t%ju PMTU timer%s fired\n");
701172720Srrs	p(sctps_timoshutdownack, "\t\t%ju shutdown ack timer%s fired\n");
702172720Srrs	p(sctps_timoshutdownguard, "\t\t%ju shutdown guard timer%s fired\n");
703172720Srrs	p(sctps_timostrmrst, "\t\t%ju stream reset timer%s fired\n");
704172103Srrs	p(sctps_timoearlyfr, "\t\t%ju early FR timer%s fired\n");
705172103Srrs	p1a(sctps_timoasconf, "\t\t%ju an asconf timer fired\n");
706172103Srrs	p1a(sctps_timoautoclose, "\t\t%ju auto close timer fired\n");
707172720Srrs	p(sctps_timoassockill, "\t\t%ju asoc free timer%s expired\n");
708172103Srrs	p(sctps_timoinpkill, "\t\t%ju inp free timer%s expired\n");
709170461Srrs
710170461Srrs#if 0
711170461Srrs	/*
712170461Srrs	 * Early fast retransmission counters
713170461Srrs	 */
714172720Srrs	p(sctps_earlyfrstart, "\t%ju TODO:sctps_earlyfrstart\n");
715172720Srrs	p(sctps_earlyfrstop, "\t%ju TODO:sctps_earlyfrstop\n");
716172720Srrs	p(sctps_earlyfrmrkretrans, "\t%ju TODO:sctps_earlyfrmrkretrans\n");
717172720Srrs	p(sctps_earlyfrstpout, "\t%ju TODO:sctps_earlyfrstpout\n");
718172720Srrs	p(sctps_earlyfrstpidsck1, "\t%ju TODO:sctps_earlyfrstpidsck1\n");
719172720Srrs	p(sctps_earlyfrstpidsck2, "\t%ju TODO:sctps_earlyfrstpidsck2\n");
720172720Srrs	p(sctps_earlyfrstpidsck3, "\t%ju TODO:sctps_earlyfrstpidsck3\n");
721172720Srrs	p(sctps_earlyfrstpidsck4, "\t%ju TODO:sctps_earlyfrstpidsck4\n");
722172720Srrs	p(sctps_earlyfrstrid, "\t%ju TODO:sctps_earlyfrstrid\n");
723172720Srrs	p(sctps_earlyfrstrout, "\t%ju TODO:sctps_earlyfrstrout\n");
724172720Srrs	p(sctps_earlyfrstrtmr, "\t%ju TODO:sctps_earlyfrstrtmr\n");
725170461Srrs#endif
726170461Srrs
727170461Srrs	/*
728170461Srrs	 * Others
729170461Srrs	 */
730172720Srrs	p1a(sctps_hdrops, "\t%ju packet shorter than header\n");
731172720Srrs	p1a(sctps_badsum, "\t%ju checksum error\n");
732172103Srrs	p1a(sctps_noport, "\t%ju no endpoint for port\n");
733172103Srrs	p1a(sctps_badvtag, "\t%ju bad v-tag\n");
734172103Srrs	p1a(sctps_badsid, "\t%ju bad SID\n");
735172103Srrs	p1a(sctps_nomem, "\t%ju no memory\n");
736172103Srrs	p1a(sctps_fastretransinrtt, "\t%ju number of multiple FR in a RTT "
737170461Srrs	    "window\n");
738170461Srrs#if 0
739172720Srrs	p(sctps_markedretrans, "\t%ju TODO:sctps_markedretrans\n");
740170461Srrs#endif
741172720Srrs	p1a(sctps_naglesent, "\t%ju RFC813 allowed sending\n");
742172720Srrs	p1a(sctps_naglequeued, "\t%ju RFC813 does not allow sending\n");
743178252Srrs	p1a(sctps_maxburstqueued, "\t%ju times max burst prohibited sending\n");
744172720Srrs	p1a(sctps_ifnomemqueued, "\t%ju look ahead tells us no memory in "
745170882Srrs	    "interface\n");
746172720Srrs	p(sctps_windowprobed, "\t%ju number%s of window probes sent\n");
747172103Srrs	p(sctps_lowlevelerr, "\t%ju time%s an output error to clamp "
748178252Srrs	    "down on next user send\n");
749172103Srrs	p(sctps_lowlevelerrusr, "\t%ju time%s sctp_senderrors were "
750170882Srrs	    "caused from a user\n");
751172103Srrs	p(sctps_datadropchklmt, "\t%ju number of in data drop%s due to "
752170461Srrs	    "chunk limit reached\n");
753172103Srrs	p(sctps_datadroprwnd, "\t%ju number of in data drop%s due to rwnd "
754170461Srrs	    "limit reached\n");
755172103Srrs	p(sctps_ecnereducedcwnd, "\t%ju time%s a ECN reduced "
756170461Srrs	    "the cwnd\n");
757172720Srrs	p1a(sctps_vtagexpress, "\t%ju used express lookup via vtag\n");
758178252Srrs	p1a(sctps_vtagbogus, "\t%ju collision in express lookup\n");
759172103Srrs	p(sctps_primary_randry, "\t%ju time%s the sender ran dry "
760170461Srrs	    "of user data on primary\n");
761172103Srrs	p1a(sctps_cmt_randry, "\t%ju same for above\n");
762172103Srrs	p(sctps_slowpath_sack, "\t%ju sack%s the slow way\n");
763172720Srrs	p(sctps_wu_sacks_sent, "\t%ju window update only sack%s sent\n");
764172720Srrs	p(sctps_sends_with_flags, "\t%ju send%s with sinfo_flags !=0\n");
765172720Srrs	p(sctps_sends_with_unord, "\t%ju unordered send%s\n");
766172720Srrs	p(sctps_sends_with_eof, "\t%ju send%s with EOF flag set\n");
767172720Srrs	p(sctps_sends_with_abort, "\t%ju send%s with ABORT flag set\n");
768172103Srrs	p(sctps_protocol_drain_calls, "\t%ju time%s protocol drain called\n");
769172103Srrs	p(sctps_protocol_drains_done, "\t%ju time%s we did a protocol "
770170882Srrs	    "drain\n");
771172103Srrs	p(sctps_read_peeks, "\t%ju time%s recv was called with peek\n");
772172103Srrs	p(sctps_cached_chk, "\t%ju cached chunk%s used\n");
773172720Srrs	p1a(sctps_cached_strmoq, "\t%ju cached stream oq's used\n");
774172720Srrs	p(sctps_left_abandon, "\t%ju unread message%s abandonded by close\n");
775172720Srrs	p1a(sctps_send_burst_avoid, "\t%ju send burst avoidance, already "
776170461Srrs	    "max burst inflight to net\n");
777172720Srrs	p1a(sctps_send_cwnd_avoid, "\t%ju send cwnd full avoidance, already "
778172720Srrs	    "max burst inflight to net\n");
779172103Srrs	p(sctps_fwdtsn_map_over, "\t%ju number of map array over-run%s via "
780170461Srrs	    "fwd-tsn's\n");
781170882Srrs
782170882Srrs#undef p
783170882Srrs#undef p1a
784170461Srrs}
785170461Srrs
786170461Srrs#endif /* SCTP */
787