1*a2f04351SSebastien Roy /*
2*a2f04351SSebastien Roy  * CDDL HEADER START
3*a2f04351SSebastien Roy  *
4*a2f04351SSebastien Roy  * This file and its contents are supplied under the terms of the
5*a2f04351SSebastien Roy  * Common Development and Distribution License ("CDDL"), version 1.0.
6*a2f04351SSebastien Roy  * You may only use this file in accordance with the terms of version
7*a2f04351SSebastien Roy  * 1.0 of the CDDL.
8*a2f04351SSebastien Roy  *
9*a2f04351SSebastien Roy  * A full copy of the text of the CDDL should have accompanied this
10*a2f04351SSebastien Roy  * source.  A copy of the CDDL is also available via the Internet at
11*a2f04351SSebastien Roy  * http://www.illumos.org/license/CDDL.
12*a2f04351SSebastien Roy  *
13*a2f04351SSebastien Roy  * CDDL HEADER END
14*a2f04351SSebastien Roy  */
15*a2f04351SSebastien Roy /*
16*a2f04351SSebastien Roy  * Copyright (c) 2015, 2016 by Delphix. All rights reserved.
17*a2f04351SSebastien Roy  */
18*a2f04351SSebastien Roy 
19*a2f04351SSebastien Roy #include <err.h>
20*a2f04351SSebastien Roy #include <stdio.h>
21*a2f04351SSebastien Roy #include <errno.h>
22*a2f04351SSebastien Roy #include <getopt.h>
23*a2f04351SSebastien Roy #include <stdlib.h>
24*a2f04351SSebastien Roy #include <stddef.h>
25*a2f04351SSebastien Roy #include <strings.h>
26*a2f04351SSebastien Roy #include <unistd.h>
27*a2f04351SSebastien Roy #include <libgen.h>
28*a2f04351SSebastien Roy #include <libintl.h>
29*a2f04351SSebastien Roy #include <limits.h>
30*a2f04351SSebastien Roy #include <locale.h>
31*a2f04351SSebastien Roy #include <langinfo.h>
32*a2f04351SSebastien Roy #include <sys/types.h>
33*a2f04351SSebastien Roy #include <sys/socket.h>
34*a2f04351SSebastien Roy #include <netdb.h>
35*a2f04351SSebastien Roy #include <sys/varargs.h>
36*a2f04351SSebastien Roy #include <ofmt.h>
37*a2f04351SSebastien Roy #include <inet/tcp.h>
38*a2f04351SSebastien Roy #include <netinet/in.h>
39*a2f04351SSebastien Roy #include <inet/mib2.h>
40*a2f04351SSebastien Roy #include "connstat.h"
41*a2f04351SSebastien Roy #include "connstat_mib.h"
42*a2f04351SSebastien Roy #include "connstat_tcp.h"
43*a2f04351SSebastien Roy 
44*a2f04351SSebastien Roy #define	DEFAULT_PROTO	"tcp"
45*a2f04351SSebastien Roy 
46*a2f04351SSebastien Roy static const char *invalid_v4v6_msg =
47*a2f04351SSebastien Roy 	"Invalid combination of IPv4 and IPv6 arguments\n";
48*a2f04351SSebastien Roy 
49*a2f04351SSebastien Roy static const char *invalid_T_msg =
50*a2f04351SSebastien Roy 	"Invalid -T arg \"%s\". Must be \"u\" or \"d\"\n";
51*a2f04351SSebastien Roy 
52*a2f04351SSebastien Roy static const struct option longopts[] = {
53*a2f04351SSebastien Roy 	{ "count",	required_argument,	0, 'c'	},
54*a2f04351SSebastien Roy 	{ "established",	no_argument,	0, 'e'	},
55*a2f04351SSebastien Roy 	{ "filter",	required_argument,	0, 'F'	},
56*a2f04351SSebastien Roy 	{ "help",	no_argument,		0, 'h'	},
57*a2f04351SSebastien Roy 	{ "interval",	required_argument,	0, 'i'	},
58*a2f04351SSebastien Roy 	{ "ipv4",	no_argument,		0, '4'	},
59*a2f04351SSebastien Roy 	{ "ipv6",	no_argument,		0, '6'	},
60*a2f04351SSebastien Roy 	{ "no-loopback",	no_argument,	0, 'L'	},
61*a2f04351SSebastien Roy 	{ "output",	required_argument,	0, 'o'	},
62*a2f04351SSebastien Roy 	{ "parsable",	no_argument,		0, 'P'	},
63*a2f04351SSebastien Roy 	{ "protocol",	required_argument,	0, 'p'	},
64*a2f04351SSebastien Roy 	{ "timestamp",	required_argument,	0, 'T'	},
65*a2f04351SSebastien Roy 	{ NULL, 0, 0, 0 }
66*a2f04351SSebastien Roy };
67*a2f04351SSebastien Roy 
68*a2f04351SSebastien Roy static connstat_proto_t connstat_protos[] = {
69*a2f04351SSebastien Roy 	CONNSTAT_TCP_PROTO,
70*a2f04351SSebastien Roy 	{ NULL, NULL, 0, 0, 0, NULL, NULL, NULL }
71*a2f04351SSebastien Roy };
72*a2f04351SSebastien Roy 
73*a2f04351SSebastien Roy typedef enum { NOTIMESTAMP, UTIMESTAMP, DTIMESTAMP } timestamp_fmt_t;
74*a2f04351SSebastien Roy 
75*a2f04351SSebastien Roy static void	die(const char *, ...) __NORETURN;
76*a2f04351SSebastien Roy static void	process_filter(char *, connstat_conn_attr_t *, uint_t *);
77*a2f04351SSebastien Roy static void	show_stats(connstat_proto_t *, ofmt_handle_t, uint_t,
78*a2f04351SSebastien Roy     connstat_conn_attr_t *, timestamp_fmt_t, uint_t, uint_t);
79*a2f04351SSebastien Roy 
80*a2f04351SSebastien Roy static void __NORETURN
usage(int code)81*a2f04351SSebastien Roy usage(int code)
82*a2f04351SSebastien Roy {
83*a2f04351SSebastien Roy 	static const char *opts[] = {
84*a2f04351SSebastien Roy 		"-4, --ipv4             Only display IPv4 connections",
85*a2f04351SSebastien Roy 		"-6, --ipv6             Only display IPv6 connections",
86*a2f04351SSebastien Roy 		"-c, --count=COUNT      Only print COUNT reports",
87*a2f04351SSebastien Roy 		"-e, --established      Only display established connections",
88*a2f04351SSebastien Roy 		"-F, --filter=FILTER    Only display connections that match "
89*a2f04351SSebastien Roy 		    "FILTER",
90*a2f04351SSebastien Roy 		"-h, --help             Print this help",
91*a2f04351SSebastien Roy 		"-i, --interval=SECONDS Report once every SECONDS seconds",
92*a2f04351SSebastien Roy 		"-L, --no-loopback      Omit loopback connections",
93*a2f04351SSebastien Roy 		"-o, --output=FIELDS    Restrict output to the comma-separated "
94*a2f04351SSebastien Roy 		    "list of fields\n"
95*a2f04351SSebastien Roy 		    "                         specified",
96*a2f04351SSebastien Roy 		"-P, --parsable         Parsable output mode",
97*a2f04351SSebastien Roy 		"-T, --timestamp=TYPE   Display a timestamp for each iteration",
98*a2f04351SSebastien Roy 		NULL
99*a2f04351SSebastien Roy 	};
100*a2f04351SSebastien Roy 
101*a2f04351SSebastien Roy 	(void) fprintf(stderr, gettext("usage: "));
102*a2f04351SSebastien Roy 	(void) fprintf(stderr,
103*a2f04351SSebastien Roy 	    gettext("%s [-eLP] [-4|-6] [-T d|u] [-F <filter>]\n"
104*a2f04351SSebastien Roy 	    "               [-i <interval> [-c <count>]] [-o <field>[,...]]\n"),
105*a2f04351SSebastien Roy 	    getprogname());
106*a2f04351SSebastien Roy 
107*a2f04351SSebastien Roy 	(void) fprintf(stderr, gettext("\nOptions:\n"));
108*a2f04351SSebastien Roy 	for (const char **optp = opts; *optp != NULL; optp++) {
109*a2f04351SSebastien Roy 		(void) fprintf(stderr, "  %s\n", gettext(*optp));
110*a2f04351SSebastien Roy 	}
111*a2f04351SSebastien Roy 
112*a2f04351SSebastien Roy 	(void) fprintf(stderr, gettext("\nFilter:\n"));
113*a2f04351SSebastien Roy 	(void) fprintf(stderr, gettext("  The FILTER argument for the -F "
114*a2f04351SSebastien Roy 	    "option is of the form:\n"
115*a2f04351SSebastien Roy 	    "    <field>=<value>,[<field>=<value>,...]\n"));
116*a2f04351SSebastien Roy 	(void) fprintf(stderr, gettext("  Filterable fields are laddr, lport, "
117*a2f04351SSebastien Roy 	    "raddr, rport, and state.\n"));
118*a2f04351SSebastien Roy 
119*a2f04351SSebastien Roy 	(void) fprintf(stderr, gettext("\nFields:\n"));
120*a2f04351SSebastien Roy 	(void) fprintf(stderr, gettext(
121*a2f04351SSebastien Roy 	    "  laddr           Local IP address\n"
122*a2f04351SSebastien Roy 	    "  raddr           Remote IP address\n"
123*a2f04351SSebastien Roy 	    "  lport           Local port\n"
124*a2f04351SSebastien Roy 	    "  rport           Remote port\n"
125*a2f04351SSebastien Roy 	    "  inbytes         Total bytes received\n"
126*a2f04351SSebastien Roy 	    "  insegs          Total segments received\n"
127*a2f04351SSebastien Roy 	    "  inunorderbytes  Bytes received out of order\n"
128*a2f04351SSebastien Roy 	    "  inunordersegs   Segments received out of order\n"
129*a2f04351SSebastien Roy 	    "  outbytes        Total bytes sent\n"
130*a2f04351SSebastien Roy 	    "  outsegs         Total segments sent\n"
131*a2f04351SSebastien Roy 	    "  retransbytes    Bytes retransmitted\n"
132*a2f04351SSebastien Roy 	    "  retranssegs     Segments retransmitted\n"
133*a2f04351SSebastien Roy 	    "  suna            Current unacknowledged bytes sent\n"
134*a2f04351SSebastien Roy 	    "  unsent          Unsent bytes on the transmit queue\n"
135*a2f04351SSebastien Roy 	    "  swnd            Send window size (peer's receive window)\n"
136*a2f04351SSebastien Roy 	    "  cwnd            Congestion window size\n"
137*a2f04351SSebastien Roy 	    "  rwnd            Receive window size\n"
138*a2f04351SSebastien Roy 	    "  mss             Maximum segment size\n"
139*a2f04351SSebastien Roy 	    "  rto             Retransmission timeout (ms)\n"
140*a2f04351SSebastien Roy 	    "  rtt             Smoothed round-trip time (us)\n"
141*a2f04351SSebastien Roy 	    "  rtts            Sum round-trip time (us)\n"
142*a2f04351SSebastien Roy 	    "  rttc            Count of round-trip times\n"
143*a2f04351SSebastien Roy 	    "  state           Connection state\n"));
144*a2f04351SSebastien Roy 	exit(code);
145*a2f04351SSebastien Roy }
146*a2f04351SSebastien Roy 
147*a2f04351SSebastien Roy static connstat_proto_t *
getproto(const char * proto)148*a2f04351SSebastien Roy getproto(const char *proto)
149*a2f04351SSebastien Roy {
150*a2f04351SSebastien Roy 	for (connstat_proto_t *current = &connstat_protos[0];
151*a2f04351SSebastien Roy 	    current->csp_proto != NULL; current++) {
152*a2f04351SSebastien Roy 		if (strcasecmp(proto, current->csp_proto) == 0) {
153*a2f04351SSebastien Roy 			return (current);
154*a2f04351SSebastien Roy 		}
155*a2f04351SSebastien Roy 	}
156*a2f04351SSebastien Roy 	return (NULL);
157*a2f04351SSebastien Roy }
158*a2f04351SSebastien Roy 
159*a2f04351SSebastien Roy int
main(int argc,char * argv[])160*a2f04351SSebastien Roy main(int argc, char *argv[])
161*a2f04351SSebastien Roy {
162*a2f04351SSebastien Roy 	int option;
163*a2f04351SSebastien Roy 	int count = 0;
164*a2f04351SSebastien Roy 	int interval = 0;
165*a2f04351SSebastien Roy 	const char *errstr = NULL;
166*a2f04351SSebastien Roy 	char *fields = NULL;
167*a2f04351SSebastien Roy 	char *filterstr = NULL;
168*a2f04351SSebastien Roy 	connstat_conn_attr_t filter = {0};
169*a2f04351SSebastien Roy 	char *protostr = DEFAULT_PROTO;
170*a2f04351SSebastien Roy 	connstat_proto_t *proto;
171*a2f04351SSebastien Roy 	ofmt_handle_t ofmt;
172*a2f04351SSebastien Roy 	ofmt_status_t oferr;
173*a2f04351SSebastien Roy 	char oferrbuf[OFMT_BUFSIZE];
174*a2f04351SSebastien Roy 	uint_t ofmtflags = OFMT_NOHEADER;
175*a2f04351SSebastien Roy 	uint_t flags = CS_LOOPBACK | CS_IPV4 | CS_IPV6;
176*a2f04351SSebastien Roy 	timestamp_fmt_t timestamp_fmt = NOTIMESTAMP;
177*a2f04351SSebastien Roy 
178*a2f04351SSebastien Roy 	(void) setlocale(LC_ALL, "");
179*a2f04351SSebastien Roy #if !defined(TEXT_DOMAIN)
180*a2f04351SSebastien Roy #define	TEXT_DOMAIN "SYS_TEST"
181*a2f04351SSebastien Roy #endif
182*a2f04351SSebastien Roy 	(void) textdomain(TEXT_DOMAIN);
183*a2f04351SSebastien Roy 
184*a2f04351SSebastien Roy 	setprogname(basename(argv[0]));
185*a2f04351SSebastien Roy 
186*a2f04351SSebastien Roy 	while ((option = getopt_long(argc, argv, "c:eF:hi:Lo:Pp:T:46",
187*a2f04351SSebastien Roy 	    longopts, NULL)) != -1) {
188*a2f04351SSebastien Roy 		switch (option) {
189*a2f04351SSebastien Roy 		case 'c':
190*a2f04351SSebastien Roy 			count = strtonum(optarg, 1, INT_MAX, &errstr);
191*a2f04351SSebastien Roy 			if (errstr != NULL) {
192*a2f04351SSebastien Roy 				(void) fprintf(stderr, gettext(
193*a2f04351SSebastien Roy 				    "error parsing -c argument (%s): %s\n"),
194*a2f04351SSebastien Roy 				    optarg, errstr);
195*a2f04351SSebastien Roy 				usage(1);
196*a2f04351SSebastien Roy 			}
197*a2f04351SSebastien Roy 			break;
198*a2f04351SSebastien Roy 		case 'e':
199*a2f04351SSebastien Roy 			flags |= CS_STATE;
200*a2f04351SSebastien Roy 			filter.ca_state = TCPS_ESTABLISHED;
201*a2f04351SSebastien Roy 			break;
202*a2f04351SSebastien Roy 		case 'F':
203*a2f04351SSebastien Roy 			filterstr = optarg;
204*a2f04351SSebastien Roy 			break;
205*a2f04351SSebastien Roy 		case 'i':
206*a2f04351SSebastien Roy 			interval = strtonum(optarg, 1, INT_MAX, &errstr);
207*a2f04351SSebastien Roy 			if (errstr != NULL) {
208*a2f04351SSebastien Roy 				(void) fprintf(stderr, gettext(
209*a2f04351SSebastien Roy 				    "error parsing -i argument (%s): %s\n"),
210*a2f04351SSebastien Roy 				    optarg, errstr);
211*a2f04351SSebastien Roy 				usage(1);
212*a2f04351SSebastien Roy 			}
213*a2f04351SSebastien Roy 			break;
214*a2f04351SSebastien Roy 		case 'L':
215*a2f04351SSebastien Roy 			flags &= ~CS_LOOPBACK;
216*a2f04351SSebastien Roy 			break;
217*a2f04351SSebastien Roy 		case 'o':
218*a2f04351SSebastien Roy 			fields = optarg;
219*a2f04351SSebastien Roy 			break;
220*a2f04351SSebastien Roy 		case 'P':
221*a2f04351SSebastien Roy 			ofmtflags |= OFMT_PARSABLE;
222*a2f04351SSebastien Roy 			flags |= CS_PARSABLE;
223*a2f04351SSebastien Roy 			break;
224*a2f04351SSebastien Roy 		case 'p':
225*a2f04351SSebastien Roy 			/*
226*a2f04351SSebastien Roy 			 * -p is an undocumented flag whose only supported
227*a2f04351SSebastien Roy 			 * argument is "tcp". The idea is to reserve this
228*a2f04351SSebastien Roy 			 * flag for potential future use in case connstat
229*a2f04351SSebastien Roy 			 * is extended to support stats for other protocols.
230*a2f04351SSebastien Roy 			 */
231*a2f04351SSebastien Roy 			protostr = optarg;
232*a2f04351SSebastien Roy 			break;
233*a2f04351SSebastien Roy 		case 'T':
234*a2f04351SSebastien Roy 			if (strcmp(optarg, "u") == 0) {
235*a2f04351SSebastien Roy 				timestamp_fmt = UTIMESTAMP;
236*a2f04351SSebastien Roy 			} else if (strcmp(optarg, "d") == 0) {
237*a2f04351SSebastien Roy 				timestamp_fmt = DTIMESTAMP;
238*a2f04351SSebastien Roy 			} else {
239*a2f04351SSebastien Roy 				(void) fprintf(stderr, gettext(
240*a2f04351SSebastien Roy 				    invalid_T_msg), optarg);
241*a2f04351SSebastien Roy 				usage(1);
242*a2f04351SSebastien Roy 			}
243*a2f04351SSebastien Roy 			break;
244*a2f04351SSebastien Roy 		case '4':
245*a2f04351SSebastien Roy 			if (!(flags & CS_IPV4)) {
246*a2f04351SSebastien Roy 				(void) fprintf(stderr, gettext(
247*a2f04351SSebastien Roy 				    invalid_v4v6_msg));
248*a2f04351SSebastien Roy 				usage(1);
249*a2f04351SSebastien Roy 			}
250*a2f04351SSebastien Roy 			flags &= ~CS_IPV6;
251*a2f04351SSebastien Roy 			break;
252*a2f04351SSebastien Roy 		case '6':
253*a2f04351SSebastien Roy 			if (!(flags & CS_IPV6)) {
254*a2f04351SSebastien Roy 				(void) fprintf(stderr, gettext(
255*a2f04351SSebastien Roy 				    invalid_v4v6_msg));
256*a2f04351SSebastien Roy 				usage(1);
257*a2f04351SSebastien Roy 			}
258*a2f04351SSebastien Roy 			flags &= ~CS_IPV4;
259*a2f04351SSebastien Roy 			break;
260*a2f04351SSebastien Roy 		case '?':
261*a2f04351SSebastien Roy 		default:
262*a2f04351SSebastien Roy 			usage(1);
263*a2f04351SSebastien Roy 			break;
264*a2f04351SSebastien Roy 		}
265*a2f04351SSebastien Roy 	}
266*a2f04351SSebastien Roy 
267*a2f04351SSebastien Roy 	if ((proto = getproto(protostr)) == NULL) {
268*a2f04351SSebastien Roy 		die("unknown protocol given to \"-p\": %s", protostr);
269*a2f04351SSebastien Roy 	}
270*a2f04351SSebastien Roy 
271*a2f04351SSebastien Roy 	if ((ofmtflags & OFMT_PARSABLE) && fields == NULL) {
272*a2f04351SSebastien Roy 		die("parsable output requires \"-o\"");
273*a2f04351SSebastien Roy 	}
274*a2f04351SSebastien Roy 
275*a2f04351SSebastien Roy 	if ((ofmtflags & OFMT_PARSABLE) && fields != NULL &&
276*a2f04351SSebastien Roy 	    strcasecmp(fields, "all") == 0) {
277*a2f04351SSebastien Roy 		die("\"-o all\" is invalid with parsable output");
278*a2f04351SSebastien Roy 	}
279*a2f04351SSebastien Roy 
280*a2f04351SSebastien Roy 	if (fields == NULL) {
281*a2f04351SSebastien Roy 		fields = proto->csp_default_fields;
282*a2f04351SSebastien Roy 	}
283*a2f04351SSebastien Roy 
284*a2f04351SSebastien Roy 	/* If count is specified, then interval must also be specified. */
285*a2f04351SSebastien Roy 	if (count != 0 && interval == 0) {
286*a2f04351SSebastien Roy 		die("\"-c\" requires \"-i\"");
287*a2f04351SSebastien Roy 	}
288*a2f04351SSebastien Roy 
289*a2f04351SSebastien Roy 	/* If interval is not specified, then the default count is 1. */
290*a2f04351SSebastien Roy 	if (interval == 0 && count == 0) {
291*a2f04351SSebastien Roy 		count = 1;
292*a2f04351SSebastien Roy 	}
293*a2f04351SSebastien Roy 
294*a2f04351SSebastien Roy 	if (filterstr != NULL) {
295*a2f04351SSebastien Roy 		process_filter(filterstr, &filter, &flags);
296*a2f04351SSebastien Roy 	}
297*a2f04351SSebastien Roy 
298*a2f04351SSebastien Roy 	oferr = ofmt_open(fields, proto->csp_getfields(), ofmtflags, 0, &ofmt);
299*a2f04351SSebastien Roy 	if (oferr != OFMT_SUCCESS) {
300*a2f04351SSebastien Roy 		(void) ofmt_strerror(ofmt, oferr, oferrbuf, sizeof (oferrbuf));
301*a2f04351SSebastien Roy 		die(oferrbuf);
302*a2f04351SSebastien Roy 	}
303*a2f04351SSebastien Roy 	ofmt_set_fs(ofmt, ',');
304*a2f04351SSebastien Roy 
305*a2f04351SSebastien Roy 	show_stats(proto, ofmt, flags, &filter, timestamp_fmt, interval, count);
306*a2f04351SSebastien Roy 
307*a2f04351SSebastien Roy 	ofmt_close(ofmt);
308*a2f04351SSebastien Roy 	return (0);
309*a2f04351SSebastien Roy }
310*a2f04351SSebastien Roy 
311*a2f04351SSebastien Roy /*
312*a2f04351SSebastien Roy  * Convert the input IP address literal to sockaddr of the appropriate address
313*a2f04351SSebastien Roy  * family. Preserves any potential port number that may have been set in the
314*a2f04351SSebastien Roy  * input sockaddr_storage structure.
315*a2f04351SSebastien Roy  */
316*a2f04351SSebastien Roy static void
str2sockaddr(const char * addr,struct sockaddr_storage * ss)317*a2f04351SSebastien Roy str2sockaddr(const char *addr, struct sockaddr_storage *ss)
318*a2f04351SSebastien Roy {
319*a2f04351SSebastien Roy 	struct addrinfo hints, *res;
320*a2f04351SSebastien Roy 
321*a2f04351SSebastien Roy 	bzero(&hints, sizeof (hints));
322*a2f04351SSebastien Roy 	hints.ai_flags = AI_NUMERICHOST;
323*a2f04351SSebastien Roy 	if (getaddrinfo(addr, NULL, &hints, &res) != 0) {
324*a2f04351SSebastien Roy 		die("invalid literal IP address: %s", addr);
325*a2f04351SSebastien Roy 	}
326*a2f04351SSebastien Roy 	bcopy(res->ai_addr, ss, res->ai_addrlen);
327*a2f04351SSebastien Roy 	freeaddrinfo(res);
328*a2f04351SSebastien Roy }
329*a2f04351SSebastien Roy 
330*a2f04351SSebastien Roy /*
331*a2f04351SSebastien Roy  * The filterstr argument is of the form: <attr>=<value>[,...]
332*a2f04351SSebastien Roy  * Possible attributes are laddr, raddr, lport, and rport. Parse this
333*a2f04351SSebastien Roy  * filter and store the results into the provided attribute structure.
334*a2f04351SSebastien Roy  */
335*a2f04351SSebastien Roy static void
process_filter(char * filterstr,connstat_conn_attr_t * filter,uint_t * flags)336*a2f04351SSebastien Roy process_filter(char *filterstr, connstat_conn_attr_t *filter, uint_t *flags)
337*a2f04351SSebastien Roy {
338*a2f04351SSebastien Roy 	int option;
339*a2f04351SSebastien Roy 	char *val;
340*a2f04351SSebastien Roy 	enum { F_LADDR, F_RADDR, F_LPORT, F_RPORT, F_STATE };
341*a2f04351SSebastien Roy 	static char *filter_optstr[] =
342*a2f04351SSebastien Roy 	    { "laddr", "raddr", "lport", "rport", "state", NULL };
343*a2f04351SSebastien Roy 	uint_t flag = 0;
344*a2f04351SSebastien Roy 	struct sockaddr_storage *addrp = NULL;
345*a2f04351SSebastien Roy 	const char *errstr = NULL;
346*a2f04351SSebastien Roy 	int *portp = NULL;
347*a2f04351SSebastien Roy 
348*a2f04351SSebastien Roy 	while (*filterstr != '\0') {
349*a2f04351SSebastien Roy 		option = getsubopt(&filterstr, filter_optstr, &val);
350*a2f04351SSebastien Roy 		errno = 0;
351*a2f04351SSebastien Roy 
352*a2f04351SSebastien Roy 		switch (option) {
353*a2f04351SSebastien Roy 		case F_LADDR:
354*a2f04351SSebastien Roy 			flag = CS_LADDR;
355*a2f04351SSebastien Roy 			addrp = &filter->ca_laddr;
356*a2f04351SSebastien Roy 			break;
357*a2f04351SSebastien Roy 		case F_RADDR:
358*a2f04351SSebastien Roy 			flag = CS_RADDR;
359*a2f04351SSebastien Roy 			addrp = &filter->ca_raddr;
360*a2f04351SSebastien Roy 			break;
361*a2f04351SSebastien Roy 		case F_LPORT:
362*a2f04351SSebastien Roy 			flag = CS_LPORT;
363*a2f04351SSebastien Roy 			portp = &filter->ca_lport;
364*a2f04351SSebastien Roy 			break;
365*a2f04351SSebastien Roy 		case F_RPORT:
366*a2f04351SSebastien Roy 			flag = CS_RPORT;
367*a2f04351SSebastien Roy 			portp = &filter->ca_rport;
368*a2f04351SSebastien Roy 			break;
369*a2f04351SSebastien Roy 		case F_STATE:
370*a2f04351SSebastien Roy 			flag = CS_STATE;
371*a2f04351SSebastien Roy 			break;
372*a2f04351SSebastien Roy 		default:
373*a2f04351SSebastien Roy 			usage(1);
374*a2f04351SSebastien Roy 		}
375*a2f04351SSebastien Roy 
376*a2f04351SSebastien Roy 		if (*flags & flag) {
377*a2f04351SSebastien Roy 			(void) fprintf(stderr, gettext(
378*a2f04351SSebastien Roy 			    "Ambiguous filter provided. The \"%s\" field "
379*a2f04351SSebastien Roy 			    "appears more than once.\n"),
380*a2f04351SSebastien Roy 			    filter_optstr[option]);
381*a2f04351SSebastien Roy 			usage(1);
382*a2f04351SSebastien Roy 		}
383*a2f04351SSebastien Roy 		*flags |= flag;
384*a2f04351SSebastien Roy 
385*a2f04351SSebastien Roy 		switch (flag) {
386*a2f04351SSebastien Roy 		case CS_LADDR:
387*a2f04351SSebastien Roy 		case CS_RADDR:
388*a2f04351SSebastien Roy 			str2sockaddr(val, addrp);
389*a2f04351SSebastien Roy 			if (addrp->ss_family == AF_INET) {
390*a2f04351SSebastien Roy 				if (!(*flags & CS_IPV4)) {
391*a2f04351SSebastien Roy 					(void) fprintf(stderr, gettext(
392*a2f04351SSebastien Roy 					    invalid_v4v6_msg));
393*a2f04351SSebastien Roy 					usage(1);
394*a2f04351SSebastien Roy 				}
395*a2f04351SSebastien Roy 				*flags &= ~CS_IPV6;
396*a2f04351SSebastien Roy 			} else {
397*a2f04351SSebastien Roy 				if (!(*flags & CS_IPV6)) {
398*a2f04351SSebastien Roy 					(void) fprintf(stderr, gettext(
399*a2f04351SSebastien Roy 					    invalid_v4v6_msg));
400*a2f04351SSebastien Roy 					usage(1);
401*a2f04351SSebastien Roy 				}
402*a2f04351SSebastien Roy 				*flags &= ~CS_IPV4;
403*a2f04351SSebastien Roy 			}
404*a2f04351SSebastien Roy 			break;
405*a2f04351SSebastien Roy 		case CS_LPORT:
406*a2f04351SSebastien Roy 		case CS_RPORT:
407*a2f04351SSebastien Roy 			*portp = strtonum(val, 1, UINT16_MAX, &errstr);
408*a2f04351SSebastien Roy 			if (errstr != NULL) {
409*a2f04351SSebastien Roy 				(void) fprintf(stderr, gettext(
410*a2f04351SSebastien Roy 				    "error parsing port (%s): %s\n"),
411*a2f04351SSebastien Roy 				    val, errstr);
412*a2f04351SSebastien Roy 				usage(1);
413*a2f04351SSebastien Roy 			}
414*a2f04351SSebastien Roy 			break;
415*a2f04351SSebastien Roy 		case CS_STATE:
416*a2f04351SSebastien Roy 			filter->ca_state = tcp_str2state(val);
417*a2f04351SSebastien Roy 			if (filter->ca_state < TCPS_CLOSED) {
418*a2f04351SSebastien Roy 				(void) fprintf(stderr, gettext(
419*a2f04351SSebastien Roy 				    "invalid TCP state: %s\n"), val);
420*a2f04351SSebastien Roy 				usage(1);
421*a2f04351SSebastien Roy 			}
422*a2f04351SSebastien Roy 			break;
423*a2f04351SSebastien Roy 		}
424*a2f04351SSebastien Roy 	}
425*a2f04351SSebastien Roy 
426*a2f04351SSebastien Roy 	/* Make sure that laddr and raddr are at least in the same family. */
427*a2f04351SSebastien Roy 	if ((*flags & (CS_LADDR|CS_RADDR)) == (CS_LADDR|CS_RADDR)) {
428*a2f04351SSebastien Roy 		if (filter->ca_laddr.ss_family != filter->ca_raddr.ss_family) {
429*a2f04351SSebastien Roy 			die("laddr and raddr must be of the same family.");
430*a2f04351SSebastien Roy 		}
431*a2f04351SSebastien Roy 	}
432*a2f04351SSebastien Roy }
433*a2f04351SSebastien Roy 
434*a2f04351SSebastien Roy /*
435*a2f04351SSebastien Roy  * Print timestamp as decimal representation of time_t value (-T u was
436*a2f04351SSebastien Roy  * specified) or in date(1) format (-T d was specified).
437*a2f04351SSebastien Roy  */
438*a2f04351SSebastien Roy static void
print_timestamp(timestamp_fmt_t timestamp_fmt,boolean_t parsable)439*a2f04351SSebastien Roy print_timestamp(timestamp_fmt_t timestamp_fmt, boolean_t parsable)
440*a2f04351SSebastien Roy {
441*a2f04351SSebastien Roy 	time_t t = time(NULL);
442*a2f04351SSebastien Roy 	char *pfx = parsable ? "= " : "";
443*a2f04351SSebastien Roy 	static char *fmt = NULL;
444*a2f04351SSebastien Roy 
445*a2f04351SSebastien Roy 	/* We only need to retrieve this once per invocation */
446*a2f04351SSebastien Roy 	if (fmt == NULL) {
447*a2f04351SSebastien Roy 		fmt = nl_langinfo(_DATE_FMT);
448*a2f04351SSebastien Roy 	}
449*a2f04351SSebastien Roy 
450*a2f04351SSebastien Roy 	switch (timestamp_fmt) {
451*a2f04351SSebastien Roy 	case NOTIMESTAMP:
452*a2f04351SSebastien Roy 		break;
453*a2f04351SSebastien Roy 	case UTIMESTAMP:
454*a2f04351SSebastien Roy 		(void) printf("%s%ld\n", pfx, t);
455*a2f04351SSebastien Roy 		break;
456*a2f04351SSebastien Roy 	case DTIMESTAMP: {
457*a2f04351SSebastien Roy 		char dstr[64];
458*a2f04351SSebastien Roy 		size_t len;
459*a2f04351SSebastien Roy 
460*a2f04351SSebastien Roy 		len = strftime(dstr, sizeof (dstr), fmt, localtime(&t));
461*a2f04351SSebastien Roy 		if (len > 0) {
462*a2f04351SSebastien Roy 			(void) printf("%s%s\n", pfx, dstr);
463*a2f04351SSebastien Roy 		}
464*a2f04351SSebastien Roy 		break;
465*a2f04351SSebastien Roy 	}
466*a2f04351SSebastien Roy 	default:
467*a2f04351SSebastien Roy 		abort();
468*a2f04351SSebastien Roy 		break;
469*a2f04351SSebastien Roy 	}
470*a2f04351SSebastien Roy }
471*a2f04351SSebastien Roy 
472*a2f04351SSebastien Roy static void
show_stats(connstat_proto_t * proto,ofmt_handle_t ofmt,uint_t flags,connstat_conn_attr_t * filter,timestamp_fmt_t timestamp_fmt,uint_t interval,uint_t count)473*a2f04351SSebastien Roy show_stats(connstat_proto_t *proto, ofmt_handle_t ofmt, uint_t flags,
474*a2f04351SSebastien Roy     connstat_conn_attr_t *filter, timestamp_fmt_t timestamp_fmt,
475*a2f04351SSebastien Roy     uint_t interval, uint_t count)
476*a2f04351SSebastien Roy {
477*a2f04351SSebastien Roy 	boolean_t done = B_FALSE;
478*a2f04351SSebastien Roy 	uint_t i = 0;
479*a2f04351SSebastien Roy 	int mibfd;
480*a2f04351SSebastien Roy 	conn_walk_state_t state;
481*a2f04351SSebastien Roy 
482*a2f04351SSebastien Roy 	state.cws_ofmt = ofmt;
483*a2f04351SSebastien Roy 	state.cws_flags = flags;
484*a2f04351SSebastien Roy 	state.cws_filter = *filter;
485*a2f04351SSebastien Roy 
486*a2f04351SSebastien Roy 	if ((mibfd = mibopen(proto->csp_proto)) == -1) {
487*a2f04351SSebastien Roy 		die("failed to open MIB stream: %s", strerror(errno));
488*a2f04351SSebastien Roy 	}
489*a2f04351SSebastien Roy 
490*a2f04351SSebastien Roy 	do {
491*a2f04351SSebastien Roy 		if (timestamp_fmt != NOTIMESTAMP) {
492*a2f04351SSebastien Roy 			print_timestamp(timestamp_fmt, flags & CS_PARSABLE);
493*a2f04351SSebastien Roy 		}
494*a2f04351SSebastien Roy 		if (!(flags & CS_PARSABLE)) {
495*a2f04351SSebastien Roy 			ofmt_print_header(ofmt);
496*a2f04351SSebastien Roy 		}
497*a2f04351SSebastien Roy 
498*a2f04351SSebastien Roy 		if (conn_walk(mibfd, proto, &state) != 0) {
499*a2f04351SSebastien Roy 			die("failed to fetch and print connection info");
500*a2f04351SSebastien Roy 		}
501*a2f04351SSebastien Roy 
502*a2f04351SSebastien Roy 		if (count != 0 && ++i == count) {
503*a2f04351SSebastien Roy 			done = B_TRUE;
504*a2f04351SSebastien Roy 		} else {
505*a2f04351SSebastien Roy 			(void) sleep(interval);
506*a2f04351SSebastien Roy 		}
507*a2f04351SSebastien Roy 	} while (!done);
508*a2f04351SSebastien Roy }
509*a2f04351SSebastien Roy 
510*a2f04351SSebastien Roy /*
511*a2f04351SSebastien Roy  * ofmt callbacks for printing individual fields of various types.
512*a2f04351SSebastien Roy  */
513*a2f04351SSebastien Roy boolean_t
print_string(ofmt_arg_t * ofarg,char * buf,uint_t bufsize)514*a2f04351SSebastien Roy print_string(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
515*a2f04351SSebastien Roy {
516*a2f04351SSebastien Roy 	char *value;
517*a2f04351SSebastien Roy 
518*a2f04351SSebastien Roy 	value = (char *)ofarg->ofmt_cbarg + ofarg->ofmt_id;
519*a2f04351SSebastien Roy 	(void) strlcpy(buf, value, bufsize);
520*a2f04351SSebastien Roy 	return (B_TRUE);
521*a2f04351SSebastien Roy }
522*a2f04351SSebastien Roy 
523*a2f04351SSebastien Roy boolean_t
print_uint16(ofmt_arg_t * ofarg,char * buf,uint_t bufsize)524*a2f04351SSebastien Roy print_uint16(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
525*a2f04351SSebastien Roy {
526*a2f04351SSebastien Roy 	uint16_t value;
527*a2f04351SSebastien Roy 
528*a2f04351SSebastien Roy 	/* LINTED E_BAD_PTR_CAST_ALIGN */
529*a2f04351SSebastien Roy 	value = *(uint16_t *)((char *)ofarg->ofmt_cbarg + ofarg->ofmt_id);
530*a2f04351SSebastien Roy 	(void) snprintf(buf, bufsize, "%hu", value);
531*a2f04351SSebastien Roy 	return (B_TRUE);
532*a2f04351SSebastien Roy }
533*a2f04351SSebastien Roy 
534*a2f04351SSebastien Roy boolean_t
print_uint32(ofmt_arg_t * ofarg,char * buf,uint_t bufsize)535*a2f04351SSebastien Roy print_uint32(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
536*a2f04351SSebastien Roy {
537*a2f04351SSebastien Roy 	uint32_t value;
538*a2f04351SSebastien Roy 
539*a2f04351SSebastien Roy 	/* LINTED E_BAD_PTR_CAST_ALIGN */
540*a2f04351SSebastien Roy 	value = *(uint32_t *)((char *)ofarg->ofmt_cbarg + ofarg->ofmt_id);
541*a2f04351SSebastien Roy 	(void) snprintf(buf, bufsize, "%u", value);
542*a2f04351SSebastien Roy 	return (B_TRUE);
543*a2f04351SSebastien Roy }
544*a2f04351SSebastien Roy 
545*a2f04351SSebastien Roy boolean_t
print_uint64(ofmt_arg_t * ofarg,char * buf,uint_t bufsize)546*a2f04351SSebastien Roy print_uint64(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
547*a2f04351SSebastien Roy {
548*a2f04351SSebastien Roy 	uint64_t value;
549*a2f04351SSebastien Roy 
550*a2f04351SSebastien Roy 	/* LINTED E_BAD_PTR_CAST_ALIGN */
551*a2f04351SSebastien Roy 	value = *(uint64_t *)((char *)ofarg->ofmt_cbarg + ofarg->ofmt_id);
552*a2f04351SSebastien Roy 	(void) snprintf(buf, bufsize, "%llu", value);
553*a2f04351SSebastien Roy 	return (B_TRUE);
554*a2f04351SSebastien Roy }
555*a2f04351SSebastien Roy 
556*a2f04351SSebastien Roy /* PRINTFLIKE1 */
557*a2f04351SSebastien Roy static void
die(const char * format,...)558*a2f04351SSebastien Roy die(const char *format, ...)
559*a2f04351SSebastien Roy {
560*a2f04351SSebastien Roy 	va_list alist;
561*a2f04351SSebastien Roy 
562*a2f04351SSebastien Roy 	format = gettext(format);
563*a2f04351SSebastien Roy 
564*a2f04351SSebastien Roy 	va_start(alist, format);
565*a2f04351SSebastien Roy 	verrx(1, format, alist);
566*a2f04351SSebastien Roy 	va_end(alist);
567*a2f04351SSebastien Roy }
568