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