xref: /illumos-gate/usr/src/cmd/flowadm/flowadm.c (revision f83ffe1aa13dc057aa65c36bacc83297b35f9be2)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <stdio.h>
27 #include <locale.h>
28 #include <stdarg.h>
29 #include <stdlib.h>
30 #include <fcntl.h>
31 #include <string.h>
32 #include <stropts.h>
33 #include <errno.h>
34 #include <kstat.h>
35 #include <strings.h>
36 #include <getopt.h>
37 #include <unistd.h>
38 #include <priv.h>
39 #include <netdb.h>
40 #include <libintl.h>
41 #include <libdlflow.h>
42 #include <libdllink.h>
43 #include <libdlstat.h>
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48 #include <sys/ethernet.h>
49 #include <inet/ip.h>
50 #include <inet/ip6.h>
51 #include <stddef.h>
52 
53 #define	CMD_TYPE_ANY	0xffffffff
54 #define	STR_UNDEF_VAL	"--"
55 
56 
57 /*
58  * data structures and routines for printing output.
59  */
60 
61 typedef struct print_field_s {
62 	const char	*pf_name;
63 	const char	*pf_header;
64 	uint_t		pf_width;
65 	union {
66 		uint_t	_pf_index;
67 		size_t	_pf_offset;
68 	}_pf_un;
69 #define	pf_index	_pf_un._pf_index
70 #define	pf_offset	_pf_un._pf_offset;
71 	uint_t	pf_cmdtype;
72 } print_field_t;
73 
74 typedef struct print_state_s {
75 	print_field_t	**ps_fields;
76 	uint_t		ps_nfields;
77 	boolean_t	ps_lastfield;
78 	uint_t		ps_overflow;
79 } print_state_t;
80 
81 typedef struct show_usage_state_s {
82 	boolean_t	us_plot;
83 	boolean_t	us_parseable;
84 	boolean_t	us_printheader;
85 	boolean_t	us_first;
86 	boolean_t	us_showall;
87 	print_state_t	us_print;
88 } show_usage_state_t;
89 
90 typedef char *(*print_callback_t)(print_field_t *, void *);
91 static print_field_t **parse_output_fields(char *, print_field_t *, int,
92     uint_t, uint_t *);
93 
94 static void print_header(print_state_t *);
95 static void print_field(print_state_t *, print_field_t *, const char *,
96     boolean_t);
97 
98 static void flowadm_print_output(print_state_t *, boolean_t,
99     print_callback_t, void *);
100 
101 /*
102  * helper function that, when invoked as flowadm(print_field(pf, buf)
103  * prints string which is offset by pf->pf_offset within buf.
104  */
105 static char *flowadm_print_field(print_field_t *, void *);
106 
107 #define	MAX_FIELD_LEN	32
108 
109 typedef struct show_flow_state {
110 	boolean_t		fs_firstonly;
111 	boolean_t		fs_donefirst;
112 	pktsum_t		fs_prevstats;
113 	uint32_t		fs_flags;
114 	dladm_status_t		fs_status;
115 	print_state_t		fs_print;
116 	const char		*fs_flow;
117 	const char		*fs_link;
118 	boolean_t		fs_parseable;
119 	boolean_t		fs_printheader;
120 	boolean_t		fs_persist;
121 	boolean_t		fs_stats;
122 	uint64_t		fs_mask;
123 } show_flow_state_t;
124 
125 typedef void cmdfunc_t(int, char **);
126 
127 static cmdfunc_t do_add_flow, do_remove_flow, do_init_flow, do_show_flow;
128 static cmdfunc_t do_show_flowprop, do_set_flowprop, do_reset_flowprop;
129 static cmdfunc_t do_show_usage;
130 
131 static int	show_flow(dladm_flow_attr_t *, void *);
132 static int	show_flows_onelink(dladm_handle_t, datalink_id_t, void *);
133 
134 static void	flow_stats(const char *, datalink_id_t,  uint_t, char *,
135 		    show_flow_state_t *);
136 static void	get_flow_stats(const char *, pktsum_t *);
137 static int	show_flow_stats(dladm_flow_attr_t *, void *);
138 static int	show_link_flow_stats(dladm_handle_t, datalink_id_t, void *);
139 
140 static int	remove_flow(dladm_flow_attr_t *, void *);
141 
142 static int	show_flowprop(dladm_flow_attr_t *, void *);
143 static void	show_flowprop_one_flow(void *, const char *);
144 static int	show_flowprop_onelink(dladm_handle_t, datalink_id_t, void *);
145 
146 static void	die(const char *, ...);
147 static void	die_optdup(int);
148 static void	die_opterr(int, int);
149 static void	die_dlerr(dladm_status_t, const char *, ...);
150 static void	warn(const char *, ...);
151 static void	warn_dlerr(dladm_status_t, const char *, ...);
152 
153 typedef struct	cmd {
154 	char	*c_name;
155 	void	(*c_fn)(int, char **);
156 } cmd_t;
157 
158 static cmd_t	cmds[] = {
159 	{ "add-flow", do_add_flow },
160 	{ "remove-flow", do_remove_flow },
161 	{ "show-flowprop", do_show_flowprop },
162 	{ "set-flowprop", do_set_flowprop },
163 	{ "reset-flowprop", do_reset_flowprop },
164 	{ "show-flow", do_show_flow },
165 	{ "init-flow", do_init_flow },
166 	{ "show-usage", do_show_usage }
167 };
168 
169 static const struct option longopts[] = {
170 	{"link",		required_argument,	0, 'l'},
171 	{"parseable",		no_argument,		0, 'p'},
172 	{"statistics",		no_argument,		0, 's'},
173 	{"interval",		required_argument,	0, 'i'},
174 	{"temporary",		no_argument,		0, 't'},
175 	{"root-dir",		required_argument,	0, 'R'},
176 	{ 0, 0, 0, 0 }
177 };
178 
179 static const struct option prop_longopts[] = {
180 	{"link",		required_argument,	0, 'l'},
181 	{"temporary",		no_argument,		0, 't'},
182 	{"root-dir",		required_argument,	0, 'R'},
183 	{"prop",		required_argument,	0, 'p'},
184 	{"attr",		required_argument,	0, 'a'},
185 	{ 0, 0, 0, 0 }
186 };
187 
188 /*
189  * structures for 'flowadm remove-flow'
190  */
191 
192 typedef struct remove_flow_state {
193 	boolean_t	fs_tempop;
194 	const char	*fs_altroot;
195 	dladm_status_t	fs_status;
196 } remove_flow_state_t;
197 
198 #define	PROTO_MAXSTR_LEN	7
199 #define	PORT_MAXSTR_LEN		6
200 #define	DSFIELD_MAXSTR_LEN	10
201 
202 typedef struct flow_fields_buf_s
203 {
204 	char flow_name[MAXFLOWNAMELEN];
205 	char flow_link[MAXLINKNAMELEN];
206 	char flow_ipaddr[INET6_ADDRSTRLEN+4];
207 	char flow_proto[PROTO_MAXSTR_LEN];
208 	char flow_port[PORT_MAXSTR_LEN];
209 	char flow_dsfield[DSFIELD_MAXSTR_LEN];
210 } flow_fields_buf_t;
211 
212 static print_field_t flow_fields[] = {
213 /* name,	header,		field width,	index,		cmdtype	*/
214 {  "flow",	"FLOW",		11,
215     offsetof(flow_fields_buf_t, flow_name),	CMD_TYPE_ANY},
216 {  "link",	"LINK",		11,
217     offsetof(flow_fields_buf_t, flow_link),	CMD_TYPE_ANY},
218 {  "ipaddr",	"IPADDR",	30,
219     offsetof(flow_fields_buf_t, flow_ipaddr),	CMD_TYPE_ANY},
220 {  "proto",	"PROTO",	6,
221     offsetof(flow_fields_buf_t, flow_proto),	CMD_TYPE_ANY},
222 {  "port",	 "PORT",	7,
223     offsetof(flow_fields_buf_t, flow_port),	CMD_TYPE_ANY},
224 {  "dsfld",	"DSFLD",	9,
225     offsetof(flow_fields_buf_t, flow_dsfield),	CMD_TYPE_ANY}}
226 ;
227 
228 #define	FLOW_MAX_FIELDS		(sizeof (flow_fields) / sizeof (print_field_t))
229 
230 /*
231  * structures for 'flowadm show-flowprop'
232  */
233 typedef enum {
234 	FLOWPROP_FLOW,
235 	FLOWPROP_PROPERTY,
236 	FLOWPROP_VALUE,
237 	FLOWPROP_DEFAULT,
238 	FLOWPROP_POSSIBLE
239 } flowprop_field_index_t;
240 
241 static print_field_t flowprop_fields[] = {
242 /* name,	header,		fieldwidth,	index,		cmdtype */
243 { "flow",	"FLOW",		12,	FLOWPROP_FLOW,		CMD_TYPE_ANY},
244 { "property",	"PROPERTY",	15,	FLOWPROP_PROPERTY,	CMD_TYPE_ANY},
245 { "value",	"VALUE",	14,	FLOWPROP_VALUE,		CMD_TYPE_ANY},
246 { "default",	"DEFAULT",	14,	FLOWPROP_DEFAULT,	CMD_TYPE_ANY},
247 { "possible",	"POSSIBLE",	20,	FLOWPROP_POSSIBLE,	CMD_TYPE_ANY}}
248 ;
249 #define	FLOWPROP_MAX_FIELDS					\
250 	(sizeof (flowprop_fields) / sizeof (print_field_t))
251 
252 #define	MAX_PROP_LINE		512
253 
254 typedef struct show_flowprop_state {
255 	const char		*fs_flow;
256 	datalink_id_t		fs_linkid;
257 	char			*fs_line;
258 	char			**fs_propvals;
259 	dladm_arg_list_t	*fs_proplist;
260 	boolean_t		fs_parseable;
261 	boolean_t		fs_persist;
262 	boolean_t		fs_header;
263 	dladm_status_t		fs_status;
264 	dladm_status_t		fs_retstatus;
265 	print_state_t		fs_print;
266 } show_flowprop_state_t;
267 
268 typedef struct set_flowprop_state {
269 	const char	*fs_name;
270 	boolean_t	fs_reset;
271 	boolean_t	fs_temp;
272 	dladm_status_t	fs_status;
273 } set_flowprop_state_t;
274 
275 typedef struct flowprop_args_s {
276 	show_flowprop_state_t	*fs_state;
277 	char			*fs_propname;
278 	char			*fs_flowname;
279 } flowprop_args_t;
280 /*
281  * structures for 'flowadm show-flow -s' (print statistics)
282  */
283 typedef enum {
284 	FLOW_S_FLOW,
285 	FLOW_S_IPKTS,
286 	FLOW_S_RBYTES,
287 	FLOW_S_IERRORS,
288 	FLOW_S_OPKTS,
289 	FLOW_S_OBYTES,
290 	FLOW_S_OERRORS
291 } flow_s_field_index_t;
292 
293 static print_field_t flow_s_fields[] = {
294 /* name,	header,		field width,	index,		cmdtype	*/
295 { "flow",	"FLOW",			15,	FLOW_S_FLOW,	CMD_TYPE_ANY},
296 { "ipackets",	"IPACKETS",		10,	FLOW_S_IPKTS,	CMD_TYPE_ANY},
297 { "rbytes",	"RBYTES",		8,	FLOW_S_RBYTES,	CMD_TYPE_ANY},
298 { "ierrors",	"IERRORS",		10,	FLOW_S_IERRORS,	CMD_TYPE_ANY},
299 { "opackets",	"OPACKETS",		12,	FLOW_S_OPKTS,	CMD_TYPE_ANY},
300 { "obytes",	"OBYTES",		12,	FLOW_S_OBYTES,	CMD_TYPE_ANY},
301 { "oerrors",	"OERRORS",		8,	FLOW_S_OERRORS,	CMD_TYPE_ANY}}
302 ;
303 #define	FLOW_S_MAX_FIELDS \
304 	(sizeof (flow_s_fields) / sizeof (print_field_t))
305 
306 typedef struct flow_args_s {
307 	char		*flow_s_flow;
308 	pktsum_t	*flow_s_psum;
309 } flow_args_t;
310 static char *print_flow_stats(print_field_t *, void *);
311 
312 /*
313  * structures for 'flowadm show-usage'
314  */
315 
316 typedef struct  usage_fields_buf_s {
317 	char	usage_flow[12];
318 	char	usage_duration[10];
319 	char	usage_ipackets[9];
320 	char	usage_rbytes[10];
321 	char	usage_opackets[9];
322 	char	usage_obytes[10];
323 	char	usage_bandwidth[14];
324 } usage_fields_buf_t;
325 
326 static print_field_t usage_fields[] = {
327 /* name,	header,		field width,	offset,	cmdtype		*/
328 { "flow",	"FLOW",			12,
329     offsetof(usage_fields_buf_t, usage_flow),		CMD_TYPE_ANY},
330 { "duration",	"DURATION",		10,
331     offsetof(usage_fields_buf_t, usage_duration),	CMD_TYPE_ANY},
332 { "ipackets",	"IPACKETS",		9,
333     offsetof(usage_fields_buf_t, usage_ipackets),	CMD_TYPE_ANY},
334 { "rbytes",	"RBYTES",		10,
335     offsetof(usage_fields_buf_t, usage_rbytes),		CMD_TYPE_ANY},
336 { "opackets",	"OPACKETS",		9,
337     offsetof(usage_fields_buf_t, usage_opackets),	CMD_TYPE_ANY},
338 { "obytes",	"OBYTES",		10,
339     offsetof(usage_fields_buf_t, usage_obytes),		CMD_TYPE_ANY},
340 { "bandwidth",	"BANDWIDTH",		14,
341     offsetof(usage_fields_buf_t, usage_bandwidth),	CMD_TYPE_ANY}}
342 ;
343 
344 #define	USAGE_MAX_FIELDS	(sizeof (usage_fields) / sizeof (print_field_t))
345 
346 /*
347  * structures for 'dladm show-usage link'
348  */
349 
350 typedef struct  usage_l_fields_buf_s {
351 	char	usage_l_flow[12];
352 	char	usage_l_stime[13];
353 	char	usage_l_etime[13];
354 	char	usage_l_rbytes[8];
355 	char	usage_l_obytes[8];
356 	char	usage_l_bandwidth[14];
357 } usage_l_fields_buf_t;
358 
359 static print_field_t usage_l_fields[] = {
360 /* name,	header,		field width,	offset,	cmdtype		*/
361 { "flow",	"FLOW",		12,
362     offsetof(usage_l_fields_buf_t, usage_l_flow),	CMD_TYPE_ANY},
363 { "start",	"START",	13,
364     offsetof(usage_l_fields_buf_t, usage_l_stime),	CMD_TYPE_ANY},
365 { "end",	"END",		13,
366     offsetof(usage_l_fields_buf_t, usage_l_etime),	CMD_TYPE_ANY},
367 { "rbytes",	"RBYTES",	8,
368     offsetof(usage_l_fields_buf_t, usage_l_rbytes),	CMD_TYPE_ANY},
369 { "obytes",	"OBYTES",	8,
370     offsetof(usage_l_fields_buf_t, usage_l_obytes),	CMD_TYPE_ANY},
371 { "bandwidth",	"BANDWIDTH",	14,
372     offsetof(usage_l_fields_buf_t, usage_l_bandwidth),	CMD_TYPE_ANY}}
373 ;
374 
375 #define	USAGE_L_MAX_FIELDS \
376 	(sizeof (usage_l_fields) /sizeof (print_field_t))
377 
378 #define	PRI_HI		100
379 #define	PRI_LO 		10
380 #define	PRI_NORM	50
381 
382 #define	FLOWADM_CONF	"/etc/dladm/flowadm.conf"
383 #define	BLANK_LINE(s)	((s[0] == '\0') || (s[0] == '#') || (s[0] == '\n'))
384 
385 static char *progname;
386 
387 boolean_t		t_arg = B_FALSE; /* changes are persistent */
388 char			*altroot = NULL;
389 
390 /*
391  * Handle to libdladm.  Opened in main() before the sub-command
392  * specific function is called.
393  */
394 static dladm_handle_t handle = NULL;
395 
396 static const char *attr_table[] =
397 	{"local_ip", "remote_ip", "transport", "local_port", "dsfield"};
398 
399 #define	NATTR	(sizeof (attr_table)/sizeof (char *))
400 
401 static void
402 usage(void)
403 {
404 	(void) fprintf(stderr, gettext("usage: flowadm <subcommand>"
405 	    " <args>...\n"
406 	    "    add-flow       [-t] -l <link> -a <attr>=<value>[,...]\n"
407 	    "\t\t   [-p <prop>=<value>,...] <flow>\n"
408 	    "    remove-flow    [-t] {-l <link> | <flow>}\n"
409 	    "    show-flow      [-p] [-s [-i <interval>]] [-l <link>] "
410 	    "[<flow>]\n\n"
411 	    "    set-flowprop   [-t] -p <prop>=<value>[,...] <flow>\n"
412 	    "    reset-flowprop [-t] [-p <prop>,...] <flow>\n"
413 	    "    show-flowprop  [-cP] [-l <link>] [-p <prop>,...] "
414 	    "[<flow>]\n\n"
415 	    "    show-usage     [-a] [-d | -F <format>] "
416 	    "[-s <DD/MM/YYYY,HH:MM:SS>]\n"
417 	    "\t\t   [-e <DD/MM/YYYY,HH:MM:SS>] -f <logfile> [<flow>]\n"));
418 
419 	/* close dladm handle if it was opened */
420 	if (handle != NULL)
421 		dladm_close(handle);
422 
423 	exit(1);
424 }
425 
426 int
427 main(int argc, char *argv[])
428 {
429 	int	i, arglen, cmdlen;
430 	cmd_t	*cmdp;
431 	dladm_status_t status;
432 
433 	(void) setlocale(LC_ALL, "");
434 #if !defined(TEXT_DOMAIN)
435 #define	TEXT_DOMAIN "SYS_TEST"
436 #endif
437 	(void) textdomain(TEXT_DOMAIN);
438 
439 	progname = argv[0];
440 
441 	if (argc < 2)
442 		usage();
443 
444 	for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
445 		cmdp = &cmds[i];
446 		arglen = strlen(argv[1]);
447 		cmdlen = strlen(cmdp->c_name);
448 		if ((arglen == cmdlen) && (strncmp(argv[1], cmdp->c_name,
449 		    cmdlen) == 0)) {
450 			/* Open the libdladm handle */
451 			if ((status = dladm_open(&handle)) != DLADM_STATUS_OK) {
452 				die_dlerr(status,
453 				    "could not open /dev/dld");
454 			}
455 
456 			cmdp->c_fn(argc - 1, &argv[1]);
457 
458 			dladm_close(handle);
459 			exit(EXIT_SUCCESS);
460 		}
461 	}
462 
463 	(void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"),
464 	    progname, argv[1]);
465 	usage();
466 
467 	return (0);
468 }
469 
470 static const char *
471 match_attr(char *attr)
472 {
473 	int i;
474 
475 	for (i = 0; i < NATTR; i++) {
476 		if (strlen(attr) == strlen(attr_table[i]) &&
477 		    strncmp(attr, attr_table[i], strlen(attr_table[i])) == 0) {
478 			return (attr);
479 		}
480 	}
481 	return (NULL);
482 }
483 
484 /* ARGSUSED */
485 static void
486 do_init_flow(int argc, char *argv[])
487 {
488 	dladm_status_t status;
489 
490 	status = dladm_flow_init(handle);
491 	if (status != DLADM_STATUS_OK)
492 		die_dlerr(status, "flows initialization failed");
493 }
494 
495 /* ARGSUSED */
496 static int
497 show_usage_date(dladm_usage_t *usage, void *arg)
498 {
499 	show_usage_state_t	*state = (show_usage_state_t *)arg;
500 	time_t			stime;
501 	char			timebuf[20];
502 	dladm_flow_attr_t	attr;
503 	dladm_status_t		status;
504 
505 	/*
506 	 * Only show usage information for existing flows unless '-a'
507 	 * is specified.
508 	 */
509 	if (!state->us_showall && ((status = dladm_flow_info(handle,
510 	    usage->du_name, &attr)) != DLADM_STATUS_OK)) {
511 		return (status);
512 	}
513 
514 	stime = usage->du_stime;
515 	(void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y",
516 	    localtime(&stime));
517 	(void) printf("%s\n", timebuf);
518 
519 	return (DLADM_STATUS_OK);
520 }
521 
522 static int
523 show_usage_time(dladm_usage_t *usage, void *arg)
524 {
525 	show_usage_state_t	*state = (show_usage_state_t *)arg;
526 	char			buf[DLADM_STRSIZE];
527 	usage_l_fields_buf_t 	ubuf;
528 	time_t			time;
529 	double			bw;
530 	dladm_flow_attr_t	attr;
531 	dladm_status_t		status;
532 
533 	/*
534 	 * Only show usage information for existing flows unless '-a'
535 	 * is specified.
536 	 */
537 	if (!state->us_showall && ((status = dladm_flow_info(handle,
538 	    usage->du_name, &attr)) != DLADM_STATUS_OK)) {
539 		return (status);
540 	}
541 
542 	if (state->us_plot) {
543 		if (!state->us_printheader) {
544 			if (state->us_first) {
545 				(void) printf("# Time");
546 				state->us_first = B_FALSE;
547 			}
548 			(void) printf(" %s", usage->du_name);
549 			if (usage->du_last) {
550 				(void) printf("\n");
551 				state->us_first = B_TRUE;
552 				state->us_printheader = B_TRUE;
553 			}
554 		} else {
555 			if (state->us_first) {
556 				time = usage->du_etime;
557 				(void) strftime(buf, sizeof (buf), "%T",
558 				    localtime(&time));
559 				state->us_first = B_FALSE;
560 				(void) printf("%s", buf);
561 			}
562 			bw = (double)usage->du_bandwidth/1000;
563 			(void) printf(" %.2f", bw);
564 			if (usage->du_last) {
565 				(void) printf("\n");
566 				state->us_first = B_TRUE;
567 			}
568 		}
569 		return (DLADM_STATUS_OK);
570 	}
571 
572 	bzero(&ubuf, sizeof (ubuf));
573 
574 	(void) snprintf(ubuf.usage_l_flow, sizeof (ubuf.usage_l_flow), "%s",
575 	    usage->du_name);
576 	time = usage->du_stime;
577 	(void) strftime(buf, sizeof (buf), "%T", localtime(&time));
578 	(void) snprintf(ubuf.usage_l_stime, sizeof (ubuf.usage_l_stime), "%s",
579 	    buf);
580 	time = usage->du_etime;
581 	(void) strftime(buf, sizeof (buf), "%T", localtime(&time));
582 	(void) snprintf(ubuf.usage_l_etime, sizeof (ubuf.usage_l_etime), "%s",
583 	    buf);
584 	(void) snprintf(ubuf.usage_l_rbytes, sizeof (ubuf.usage_l_rbytes),
585 	    "%llu", usage->du_rbytes);
586 	(void) snprintf(ubuf.usage_l_obytes, sizeof (ubuf.usage_l_obytes),
587 	    "%llu", usage->du_obytes);
588 	(void) snprintf(ubuf.usage_l_bandwidth, sizeof (ubuf.usage_l_bandwidth),
589 	    "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf));
590 
591 	if (!state->us_parseable && !state->us_printheader) {
592 		print_header(&state->us_print);
593 		state->us_printheader = B_TRUE;
594 	}
595 
596 	flowadm_print_output(&state->us_print, state->us_parseable,
597 	    flowadm_print_field, (void *)&ubuf);
598 
599 	return (DLADM_STATUS_OK);
600 }
601 
602 static int
603 show_usage_res(dladm_usage_t *usage, void *arg)
604 {
605 	show_usage_state_t	*state = (show_usage_state_t *)arg;
606 	char			buf[DLADM_STRSIZE];
607 	usage_fields_buf_t	ubuf;
608 	dladm_flow_attr_t	attr;
609 	dladm_status_t		status;
610 
611 	/*
612 	 * Only show usage information for existing flows unless '-a'
613 	 * is specified.
614 	 */
615 	if (!state->us_showall && ((status = dladm_flow_info(handle,
616 	    usage->du_name, &attr)) != DLADM_STATUS_OK)) {
617 		return (status);
618 	}
619 
620 	bzero(&ubuf, sizeof (ubuf));
621 
622 	(void) snprintf(ubuf.usage_flow, sizeof (ubuf.usage_flow), "%s",
623 	    usage->du_name);
624 	(void) snprintf(ubuf.usage_duration, sizeof (ubuf.usage_duration),
625 	    "%llu", usage->du_duration);
626 	(void) snprintf(ubuf.usage_ipackets, sizeof (ubuf.usage_ipackets),
627 	    "%llu", usage->du_ipackets);
628 	(void) snprintf(ubuf.usage_rbytes, sizeof (ubuf.usage_rbytes),
629 	    "%llu", usage->du_rbytes);
630 	(void) snprintf(ubuf.usage_opackets, sizeof (ubuf.usage_opackets),
631 	    "%llu", usage->du_opackets);
632 	(void) snprintf(ubuf.usage_obytes, sizeof (ubuf.usage_obytes),
633 	    "%llu", usage->du_obytes);
634 	(void) snprintf(ubuf.usage_bandwidth, sizeof (ubuf.usage_bandwidth),
635 	    "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf));
636 
637 	if (!state->us_parseable && !state->us_printheader) {
638 		print_header(&state->us_print);
639 		state->us_printheader = B_TRUE;
640 	}
641 
642 	flowadm_print_output(&state->us_print, state->us_parseable,
643 	    flowadm_print_field, (void *)&ubuf);
644 
645 	return (DLADM_STATUS_OK);
646 }
647 
648 static boolean_t
649 valid_formatspec(char *formatspec_str)
650 {
651 	if (strcmp(formatspec_str, "gnuplot") == 0)
652 		return (B_TRUE);
653 	return (B_FALSE);
654 }
655 
656 /* ARGSUSED */
657 static void
658 do_show_usage(int argc, char *argv[])
659 {
660 	char			*file = NULL;
661 	int			opt;
662 	dladm_status_t		status;
663 	boolean_t		d_arg = B_FALSE;
664 	char			*stime = NULL;
665 	char			*etime = NULL;
666 	char			*resource = NULL;
667 	show_usage_state_t	state;
668 	boolean_t		o_arg = B_FALSE;
669 	boolean_t		F_arg = B_FALSE;
670 	char			*fields_str = NULL;
671 	char			*formatspec_str = NULL;
672 	print_field_t		**fields;
673 	uint_t			nfields;
674 	char			*all_fields =
675 	    "flow,duration,ipackets,rbytes,opackets,obytes,bandwidth";
676 	char			*all_l_fields =
677 	    "flow,start,end,rbytes,obytes,bandwidth";
678 
679 	bzero(&state, sizeof (show_usage_state_t));
680 	state.us_parseable = B_FALSE;
681 	state.us_printheader = B_FALSE;
682 	state.us_plot = B_FALSE;
683 	state.us_first = B_TRUE;
684 
685 	while ((opt = getopt(argc, argv, "das:e:o:f:F:")) != -1) {
686 		switch (opt) {
687 		case 'd':
688 			d_arg = B_TRUE;
689 			break;
690 		case 'a':
691 			state.us_showall = B_TRUE;
692 			break;
693 		case 'f':
694 			file = optarg;
695 			break;
696 		case 's':
697 			stime = optarg;
698 			break;
699 		case 'e':
700 			etime = optarg;
701 			break;
702 		case 'o':
703 			o_arg = B_TRUE;
704 			fields_str = optarg;
705 			break;
706 		case 'F':
707 			state.us_plot = F_arg = B_TRUE;
708 			formatspec_str = optarg;
709 			break;
710 		default:
711 			die_opterr(optopt, opt);
712 		}
713 	}
714 
715 	if (file == NULL)
716 		die("show-usage requires a file");
717 
718 	if (optind == (argc-1)) {
719 		dladm_flow_attr_t	attr;
720 
721 		if (!state.us_showall &&
722 		    dladm_flow_info(handle, resource, &attr) !=
723 		    DLADM_STATUS_OK) {
724 			die("invalid flow: '%s'", resource);
725 		}
726 		resource = argv[optind];
727 	}
728 
729 	if (resource == NULL && stime == NULL && etime == NULL) {
730 		if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
731 			fields_str = all_fields;
732 		fields = parse_output_fields(fields_str, usage_fields,
733 		    USAGE_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
734 	} else {
735 		if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
736 			fields_str = all_l_fields;
737 		fields = parse_output_fields(fields_str, usage_l_fields,
738 		    USAGE_L_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
739 	}
740 
741 	if (fields == NULL) {
742 		die("invalid fields(s) specified");
743 		return;
744 	}
745 	state.us_print.ps_fields = fields;
746 	state.us_print.ps_nfields = nfields;
747 
748 	if (F_arg && d_arg)
749 		die("incompatible -d and -F options");
750 
751 	if (F_arg && valid_formatspec(formatspec_str) == B_FALSE)
752 		die("Format specifier %s not supported", formatspec_str);
753 
754 	if (d_arg) {
755 		/* Print log dates */
756 		status = dladm_usage_dates(show_usage_date,
757 		    DLADM_LOGTYPE_FLOW, file, resource, &state);
758 	} else if (resource == NULL && stime == NULL && etime == NULL &&
759 	    !F_arg) {
760 		/* Print summary */
761 		status = dladm_usage_summary(show_usage_res,
762 		    DLADM_LOGTYPE_FLOW, file, &state);
763 	} else if (resource != NULL) {
764 		/* Print log entries for named resource */
765 		status = dladm_walk_usage_res(show_usage_time,
766 		    DLADM_LOGTYPE_FLOW, file, resource, stime, etime, &state);
767 	} else {
768 		/* Print time and information for each link */
769 		status = dladm_walk_usage_time(show_usage_time,
770 		    DLADM_LOGTYPE_FLOW, file, stime, etime, &state);
771 	}
772 
773 	if (status != DLADM_STATUS_OK)
774 		die_dlerr(status, "show-usage");
775 }
776 
777 static void
778 do_add_flow(int argc, char *argv[])
779 {
780 	char			devname[MAXLINKNAMELEN];
781 	char			*name = NULL;
782 	uint_t			index;
783 	datalink_id_t		linkid;
784 
785 	char			option;
786 	boolean_t		l_arg = B_FALSE;
787 	char			propstr[DLADM_STRSIZE];
788 	char			attrstr[DLADM_STRSIZE];
789 	dladm_arg_list_t	*proplist = NULL;
790 	dladm_arg_list_t	*attrlist = NULL;
791 	dladm_status_t		status;
792 
793 	bzero(propstr, DLADM_STRSIZE);
794 	bzero(attrstr, DLADM_STRSIZE);
795 
796 	while ((option = getopt_long(argc, argv, "tR:l:a:p:",
797 	    prop_longopts, NULL)) != -1) {
798 		switch (option) {
799 		case 't':
800 			t_arg = B_TRUE;
801 			break;
802 		case 'R':
803 			altroot = optarg;
804 			break;
805 		case 'l':
806 			if (strlcpy(devname, optarg,
807 			    MAXLINKNAMELEN) >= MAXLINKNAMELEN) {
808 				die("link name too long");
809 			}
810 			if (dladm_name2info(handle, devname, &linkid, NULL,
811 			    NULL, NULL) != DLADM_STATUS_OK)
812 				die("invalid link '%s'", devname);
813 			l_arg = B_TRUE;
814 			break;
815 		case 'a':
816 			(void) strlcat(attrstr, optarg, DLADM_STRSIZE);
817 			if (strlcat(attrstr, ",", DLADM_STRSIZE) >=
818 			    DLADM_STRSIZE)
819 				die("attribute list too long '%s'", attrstr);
820 			break;
821 		case 'p':
822 			(void) strlcat(propstr, optarg, DLADM_STRSIZE);
823 			if (strlcat(propstr, ",", DLADM_STRSIZE) >=
824 			    DLADM_STRSIZE)
825 				die("property list too long '%s'", propstr);
826 			break;
827 		default:
828 			die_opterr(optopt, option);
829 		}
830 	}
831 	if (!l_arg) {
832 		die("link is required");
833 	}
834 
835 	opterr = 0;
836 	index = optind;
837 
838 	if ((index != (argc - 1)) || match_attr(argv[index]) != NULL) {
839 		die("flow name is required");
840 	} else {
841 		/* get flow name; required last argument */
842 		if (strlen(argv[index]) >= MAXFLOWNAMELEN)
843 			die("flow name too long");
844 		name = argv[index];
845 	}
846 
847 	if (dladm_parse_flow_attrs(attrstr, &attrlist, B_FALSE)
848 	    != DLADM_STATUS_OK)
849 		die("invalid flow attribute specified");
850 	if (dladm_parse_flow_props(propstr, &proplist, B_FALSE)
851 	    != DLADM_STATUS_OK)
852 		die("invalid flow property specified");
853 
854 	status = dladm_flow_add(handle, linkid, attrlist, proplist, name,
855 	    t_arg, altroot);
856 	if (status != DLADM_STATUS_OK)
857 		die_dlerr(status, "add flow failed");
858 
859 	dladm_free_attrs(attrlist);
860 	dladm_free_props(proplist);
861 }
862 
863 static void
864 do_remove_flow(int argc, char *argv[])
865 {
866 	char			option;
867 	char			*flowname = NULL;
868 	char			linkname[MAXLINKNAMELEN];
869 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
870 	boolean_t		l_arg = B_FALSE;
871 	remove_flow_state_t	state;
872 	dladm_status_t		status;
873 
874 	bzero(&state, sizeof (state));
875 
876 	opterr = 0;
877 	while ((option = getopt_long(argc, argv, ":tR:l:",
878 	    longopts, NULL)) != -1) {
879 		switch (option) {
880 		case 't':
881 			t_arg = B_TRUE;
882 			break;
883 		case 'R':
884 			altroot = optarg;
885 			break;
886 		case 'l':
887 			if (strlcpy(linkname, optarg,
888 			    MAXLINKNAMELEN) >= MAXLINKNAMELEN) {
889 				die("link name too long");
890 			}
891 			if (dladm_name2info(handle, linkname, &linkid, NULL,
892 			    NULL, NULL) != DLADM_STATUS_OK) {
893 				die("invalid link '%s'", linkname);
894 			}
895 			l_arg = B_TRUE;
896 			break;
897 		default:
898 			die_opterr(optopt, option);
899 			break;
900 		}
901 	}
902 
903 	/* when link not specified get flow name */
904 	if (!l_arg) {
905 		if (optind != (argc-1)) {
906 			usage();
907 		} else {
908 			if (strlen(argv[optind]) >= MAXFLOWNAMELEN)
909 				die("flow name too long");
910 			flowname = argv[optind];
911 		}
912 		status = dladm_flow_remove(handle, flowname, t_arg, altroot);
913 	} else {
914 		/* if link is specified then flow name should not be there */
915 		if (optind == argc-1)
916 			usage();
917 		/* walk the link to find flows and remove them */
918 		state.fs_tempop = t_arg;
919 		state.fs_altroot = altroot;
920 		state.fs_status = DLADM_STATUS_OK;
921 		status = dladm_walk_flow(remove_flow, handle, linkid, &state,
922 		    B_FALSE);
923 		/*
924 		 * check if dladm_walk_flow terminated early and see if the
925 		 * walker function as any status for us
926 		 */
927 		if (status == DLADM_STATUS_OK)
928 			status = state.fs_status;
929 	}
930 
931 	if (status != DLADM_STATUS_OK)
932 		die_dlerr(status, "remove flow failed");
933 }
934 
935 /*
936  * Walker function for removing a flow through dladm_walk_flow();
937  */
938 static int
939 remove_flow(dladm_flow_attr_t *attr, void *arg)
940 {
941 	remove_flow_state_t	*state = (remove_flow_state_t *)arg;
942 
943 	state->fs_status = dladm_flow_remove(handle, attr->fa_flowname,
944 	    state->fs_tempop, state->fs_altroot);
945 
946 	if (state->fs_status == DLADM_STATUS_OK)
947 		return (DLADM_WALK_CONTINUE);
948 	else
949 		return (DLADM_WALK_TERMINATE);
950 }
951 
952 static char *
953 flowadm_print_field(print_field_t *pf, void *arg)
954 {
955 	char *value;
956 
957 	value = (char *)arg + pf->pf_offset;
958 	return (value);
959 }
960 
961 /*ARGSUSED*/
962 static dladm_status_t
963 print_flow(show_flow_state_t *state, dladm_flow_attr_t *attr,
964     flow_fields_buf_t *fbuf)
965 {
966 	char		link[MAXLINKNAMELEN];
967 	dladm_status_t	status;
968 
969 	if ((status = dladm_datalink_id2info(handle, attr->fa_linkid, NULL,
970 	    NULL, NULL, link, sizeof (link))) != DLADM_STATUS_OK) {
971 		return (status);
972 	}
973 
974 	(void) snprintf(fbuf->flow_name, sizeof (fbuf->flow_name),
975 	    "%s", attr->fa_flowname);
976 	(void) snprintf(fbuf->flow_link, sizeof (fbuf->flow_link),
977 	    "%s", link);
978 
979 	(void) dladm_flow_attr_ip2str(attr, fbuf->flow_ipaddr,
980 	    sizeof (fbuf->flow_ipaddr));
981 	(void) dladm_flow_attr_proto2str(attr, fbuf->flow_proto,
982 	    sizeof (fbuf->flow_proto));
983 	(void) dladm_flow_attr_port2str(attr, fbuf->flow_port,
984 	    sizeof (fbuf->flow_port));
985 	(void) dladm_flow_attr_dsfield2str(attr, fbuf->flow_dsfield,
986 	    sizeof (fbuf->flow_dsfield));
987 
988 	return (DLADM_STATUS_OK);
989 }
990 
991 /*
992  * Walker function for showing flow attributes through dladm_walk_flow().
993  */
994 static int
995 show_flow(dladm_flow_attr_t *attr, void *arg)
996 {
997 	show_flow_state_t	*statep = arg;
998 	dladm_status_t		status;
999 	flow_fields_buf_t	fbuf;
1000 
1001 	/*
1002 	 * first get all the flow attributes into fbuf;
1003 	 */
1004 	bzero(&fbuf, sizeof (fbuf));
1005 	status = print_flow(statep, attr, &fbuf);
1006 
1007 	if (status != DLADM_STATUS_OK)
1008 		goto done;
1009 
1010 	if (!statep->fs_parseable && !statep->fs_printheader) {
1011 		print_header(&statep->fs_print);
1012 		statep->fs_printheader = B_TRUE;
1013 	}
1014 
1015 	flowadm_print_output(&statep->fs_print, statep->fs_parseable,
1016 	    flowadm_print_field, (void *)&fbuf);
1017 
1018 done:
1019 	statep->fs_status = status;
1020 	return (DLADM_WALK_CONTINUE);
1021 }
1022 
1023 static void
1024 show_one_flow(void *arg, const char *name)
1025 {
1026 	dladm_flow_attr_t	attr;
1027 
1028 	if (dladm_flow_info(handle, name, &attr) != DLADM_STATUS_OK)
1029 		die("invalid flow: '%s'", name);
1030 	else
1031 		(void) show_flow(&attr, arg);
1032 }
1033 
1034 /*
1035  * Wrapper of dladm_walk_flow(show_flow,...) to make it usable to
1036  * dladm_walk_datalink_id(). Used for showing flow attributes for
1037  * all flows on all links.
1038  */
1039 static int
1040 show_flows_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg)
1041 {
1042 	show_flow_state_t *state = arg;
1043 
1044 	(void) dladm_walk_flow(show_flow, dh, linkid, arg, state->fs_persist);
1045 
1046 	return (DLADM_WALK_CONTINUE);
1047 }
1048 
1049 static void
1050 get_flow_stats(const char *flowname, pktsum_t *stats)
1051 {
1052 	kstat_ctl_t	*kcp;
1053 	kstat_t		*ksp;
1054 
1055 	bzero(stats, sizeof (*stats));
1056 
1057 	if ((kcp = kstat_open()) == NULL) {
1058 		warn("kstat open operation failed");
1059 		return;
1060 	}
1061 
1062 	ksp = dladm_kstat_lookup(kcp, NULL, -1, flowname, "flow");
1063 
1064 	if (ksp != NULL)
1065 		dladm_get_stats(kcp, ksp, stats);
1066 
1067 	(void) kstat_close(kcp);
1068 }
1069 
1070 
1071 static char *
1072 print_flow_stats(print_field_t *pf, void *arg)
1073 {
1074 	flow_args_t	*fargs = arg;
1075 	pktsum_t	*diff_stats = fargs->flow_s_psum;
1076 	static char	buf[DLADM_STRSIZE];
1077 
1078 	switch (pf->pf_index) {
1079 	case FLOW_S_FLOW:
1080 		(void) snprintf(buf, sizeof (buf), "%s", fargs->flow_s_flow);
1081 		break;
1082 	case FLOW_S_IPKTS:
1083 		(void) snprintf(buf, sizeof (buf), "%llu",
1084 		    diff_stats->ipackets);
1085 		break;
1086 	case FLOW_S_RBYTES:
1087 		(void) snprintf(buf, sizeof (buf), "%llu",
1088 		    diff_stats->rbytes);
1089 		break;
1090 	case FLOW_S_IERRORS:
1091 		(void) snprintf(buf, sizeof (buf), "%u",
1092 		    diff_stats->ierrors);
1093 		break;
1094 	case FLOW_S_OPKTS:
1095 		(void) snprintf(buf, sizeof (buf), "%llu",
1096 		    diff_stats->opackets);
1097 		break;
1098 	case FLOW_S_OBYTES:
1099 		(void) snprintf(buf, sizeof (buf), "%llu",
1100 		    diff_stats->obytes);
1101 		break;
1102 	case FLOW_S_OERRORS:
1103 		(void) snprintf(buf, sizeof (buf), "%u",
1104 		    diff_stats->oerrors);
1105 		break;
1106 	default:
1107 		die("invalid input");
1108 		break;
1109 	}
1110 	return (buf);
1111 }
1112 /* ARGSUSED */
1113 static int
1114 show_flow_stats(dladm_flow_attr_t *attr, void *arg)
1115 {
1116 	show_flow_state_t	*state = (show_flow_state_t *)arg;
1117 	char			*name = attr->fa_flowname;
1118 	pktsum_t		stats, diff_stats;
1119 	flow_args_t		fargs;
1120 
1121 	if (state->fs_firstonly) {
1122 		if (state->fs_donefirst)
1123 			return (DLADM_WALK_TERMINATE);
1124 		state->fs_donefirst = B_TRUE;
1125 	} else {
1126 		bzero(&state->fs_prevstats, sizeof (state->fs_prevstats));
1127 	}
1128 
1129 	get_flow_stats(name, &stats);
1130 	dladm_stats_diff(&diff_stats, &stats, &state->fs_prevstats);
1131 
1132 	fargs.flow_s_flow = name;
1133 	fargs.flow_s_psum = &diff_stats;
1134 	flowadm_print_output(&state->fs_print, state->fs_parseable,
1135 	    print_flow_stats, &fargs);
1136 
1137 	state->fs_prevstats = stats;
1138 
1139 	return (DLADM_WALK_CONTINUE);
1140 }
1141 
1142 /*
1143  * Wrapper of dladm_walk_flow(show_flow,...) to make it usable for
1144  * dladm_walk_datalink_id(). Used for showing flow stats for
1145  * all flows on all links.
1146  */
1147 static int
1148 show_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg)
1149 {
1150 	if (dladm_walk_flow(show_flow_stats, dh, linkid, arg, B_FALSE)
1151 	    == DLADM_STATUS_OK)
1152 		return (DLADM_WALK_CONTINUE);
1153 	else
1154 		return (DLADM_WALK_TERMINATE);
1155 }
1156 
1157 /* ARGSUSED */
1158 static void
1159 flow_stats(const char *flow, datalink_id_t linkid,  uint_t interval,
1160     char *fields_str, show_flow_state_t *state)
1161 {
1162 	dladm_flow_attr_t	attr;
1163 	print_field_t		**fields;
1164 	uint_t			nfields;
1165 
1166 	fields = parse_output_fields(fields_str, flow_s_fields,
1167 	    FLOW_S_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
1168 	if (fields == NULL) {
1169 		die("invalid field(s) specified");
1170 		return;
1171 	}
1172 
1173 	state->fs_print.ps_fields = fields;
1174 	state->fs_print.ps_nfields = nfields;
1175 
1176 	if (flow != NULL &&
1177 	    dladm_flow_info(handle, flow, &attr) != DLADM_STATUS_OK)
1178 		die("invalid flow %s", flow);
1179 
1180 	/*
1181 	 * If an interval is specified, continuously show the stats
1182 	 * for only the first flow.
1183 	 */
1184 	state->fs_firstonly = (interval != 0);
1185 
1186 	if (!state->fs_parseable)
1187 		print_header(&state->fs_print);
1188 	for (;;) {
1189 		state->fs_donefirst = B_FALSE;
1190 
1191 		/* Show stats for named flow */
1192 		if (flow != NULL)  {
1193 			state->fs_flow = flow;
1194 			(void) show_flow_stats(&attr, state);
1195 
1196 		/* Show all stats on a link */
1197 		} else if (linkid != DATALINK_INVALID_LINKID) {
1198 			(void) dladm_walk_flow(show_flow_stats, handle, linkid,
1199 			    state, B_FALSE);
1200 
1201 		/* Show all stats by datalink */
1202 		} else {
1203 			(void) dladm_walk_datalink_id(show_link_flow_stats,
1204 			    handle, state, DATALINK_CLASS_ALL,
1205 			    DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
1206 		}
1207 
1208 		if (interval == 0)
1209 			break;
1210 
1211 		(void) sleep(interval);
1212 	}
1213 }
1214 
1215 static void
1216 do_show_flow(int argc, char *argv[])
1217 {
1218 	char			flowname[MAXFLOWNAMELEN];
1219 	char			linkname[MAXLINKNAMELEN];
1220 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
1221 	int			option;
1222 	boolean_t		s_arg = B_FALSE;
1223 	boolean_t		S_arg = B_FALSE;
1224 	boolean_t		i_arg = B_FALSE;
1225 	boolean_t		l_arg = B_FALSE;
1226 	boolean_t		o_arg = B_FALSE;
1227 	uint32_t		interval = 0;
1228 	show_flow_state_t	state;
1229 	char			*fields_str = NULL;
1230 	print_field_t		**fields;
1231 	uint_t			nfields;
1232 	char			*all_fields =
1233 	    "flow,link,ipaddr,proto,port,dsfld";
1234 	char			*allstat_fields =
1235 	    "flow,ipackets,rbytes,ierrors,opackets,obytes,oerrors";
1236 
1237 	bzero(&state, sizeof (state));
1238 
1239 	opterr = 0;
1240 	while ((option = getopt_long(argc, argv, ":pPsSi:l:o:",
1241 	    longopts, NULL)) != -1) {
1242 		switch (option) {
1243 		case 'p':
1244 			state.fs_parseable = B_TRUE;
1245 			break;
1246 		case 'P':
1247 			state.fs_persist = B_TRUE;
1248 			break;
1249 		case 's':
1250 			if (s_arg)
1251 				die_optdup(option);
1252 
1253 			s_arg = B_TRUE;
1254 			break;
1255 		case 'S':
1256 			if (S_arg)
1257 				die_optdup(option);
1258 
1259 			S_arg = B_TRUE;
1260 			break;
1261 		case 'o':
1262 			if (o_arg)
1263 				die_optdup(option);
1264 
1265 			o_arg = B_TRUE;
1266 			fields_str = optarg;
1267 			break;
1268 		case 'i':
1269 			if (i_arg)
1270 				die_optdup(option);
1271 
1272 			i_arg = B_TRUE;
1273 
1274 			if (!dladm_str2interval(optarg, &interval))
1275 				die("invalid interval value '%s'", optarg);
1276 			break;
1277 		case 'l':
1278 			if (strlcpy(linkname, optarg, MAXLINKNAMELEN)
1279 			    >= MAXLINKNAMELEN)
1280 				die("link name too long\n");
1281 			if (dladm_name2info(handle, linkname, &linkid, NULL,
1282 			    NULL, NULL) != DLADM_STATUS_OK)
1283 				die("invalid link '%s'", linkname);
1284 			l_arg = B_TRUE;
1285 			break;
1286 		default:
1287 			die_opterr(optopt, option);
1288 			break;
1289 		}
1290 	}
1291 
1292 	if (i_arg && !(s_arg || S_arg))
1293 		die("the -i option can be used only with -s or -S");
1294 
1295 	if (s_arg && S_arg)
1296 		die("the -s option cannot be used with -S");
1297 
1298 	/* get flow name (optional last argument */
1299 	if (optind == (argc-1)) {
1300 		if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN)
1301 		    >= MAXFLOWNAMELEN)
1302 			die("flow name too long");
1303 		state.fs_flow = flowname;
1304 	}
1305 
1306 	if (S_arg) {
1307 		dladm_continuous(handle, linkid, state.fs_flow, interval,
1308 		    FLOW_REPORT);
1309 		return;
1310 	}
1311 
1312 	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) {
1313 		if (s_arg)
1314 			fields_str = allstat_fields;
1315 		else
1316 			fields_str = all_fields;
1317 	}
1318 
1319 	if (s_arg) {
1320 		flow_stats(state.fs_flow, linkid, interval, fields_str, &state);
1321 		return;
1322 	}
1323 
1324 	fields = parse_output_fields(fields_str, flow_fields, FLOW_MAX_FIELDS,
1325 	    CMD_TYPE_ANY, &nfields);
1326 
1327 	if (fields == NULL) {
1328 		die("invalid fields(s) specified");
1329 		return;
1330 	}
1331 
1332 	state.fs_print.ps_fields = fields;
1333 	state.fs_print.ps_nfields = nfields;
1334 
1335 	/* Show attributes of one flow */
1336 	if (state.fs_flow != NULL) {
1337 		show_one_flow(&state, state.fs_flow);
1338 
1339 	/* Show attributes of flows on one link */
1340 	} else if (l_arg) {
1341 		(void) show_flows_onelink(handle, linkid, &state);
1342 
1343 	/* Show attributes of all flows on all links */
1344 	} else {
1345 		(void) dladm_walk_datalink_id(show_flows_onelink, handle,
1346 		    &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
1347 		    DLADM_OPT_ACTIVE);
1348 	}
1349 }
1350 
1351 static dladm_status_t
1352 set_flowprop_persist(const char *flow, const char *prop_name, char **prop_val,
1353     uint_t val_cnt, boolean_t reset)
1354 {
1355 	dladm_status_t	status;
1356 	char		*errprop;
1357 
1358 	status = dladm_set_flowprop(handle, flow, prop_name, prop_val, val_cnt,
1359 	    DLADM_OPT_PERSIST, &errprop);
1360 
1361 	if (status != DLADM_STATUS_OK) {
1362 		warn_dlerr(status, "cannot persistently %s flow "
1363 		    "property '%s' on '%s'", reset? "reset": "set",
1364 		    errprop, flow);
1365 	}
1366 	return (status);
1367 }
1368 
1369 static void
1370 set_flowprop(int argc, char **argv, boolean_t reset)
1371 {
1372 	int			i, option;
1373 	char			errmsg[DLADM_STRSIZE];
1374 	const char		*flow = NULL;
1375 	char			propstr[DLADM_STRSIZE];
1376 	dladm_arg_list_t	*proplist = NULL;
1377 	boolean_t		temp = B_FALSE;
1378 	dladm_status_t		status = DLADM_STATUS_OK;
1379 
1380 	opterr = 0;
1381 	bzero(propstr, DLADM_STRSIZE);
1382 
1383 	while ((option = getopt_long(argc, argv, ":p:R:t",
1384 	    prop_longopts, NULL)) != -1) {
1385 		switch (option) {
1386 		case 'p':
1387 			(void) strlcat(propstr, optarg, DLADM_STRSIZE);
1388 			if (strlcat(propstr, ",", DLADM_STRSIZE) >=
1389 			    DLADM_STRSIZE)
1390 				die("property list too long '%s'", propstr);
1391 			break;
1392 		case 't':
1393 			temp = B_TRUE;
1394 			break;
1395 		case 'R':
1396 			status = dladm_set_rootdir(optarg);
1397 			if (status != DLADM_STATUS_OK) {
1398 				die_dlerr(status, "invalid directory "
1399 				    "specified");
1400 			}
1401 			break;
1402 		default:
1403 			die_opterr(optopt, option);
1404 			break;
1405 		}
1406 	}
1407 
1408 	if (optind == (argc - 1)) {
1409 		if (strlen(argv[optind]) >= MAXFLOWNAMELEN)
1410 			die("flow name too long");
1411 		flow = argv[optind];
1412 	} else if (optind != argc) {
1413 		usage();
1414 	}
1415 	if (flow == NULL)
1416 		die("flow name must be specified");
1417 
1418 	if (dladm_parse_flow_props(propstr, &proplist, reset)
1419 	    != DLADM_STATUS_OK)
1420 		die("invalid flow property specified");
1421 
1422 	if (proplist == NULL) {
1423 		char *errprop;
1424 
1425 		if (!reset)
1426 			die("flow property must be specified");
1427 
1428 		status = dladm_set_flowprop(handle, flow, NULL, NULL, 0,
1429 		    DLADM_OPT_ACTIVE, &errprop);
1430 		if (status != DLADM_STATUS_OK) {
1431 			warn_dlerr(status, "cannot reset flow property '%s' "
1432 			    "on '%s'", errprop, flow);
1433 		}
1434 		if (!temp) {
1435 			dladm_status_t	s;
1436 
1437 			s = set_flowprop_persist(flow, NULL, NULL, 0, reset);
1438 			if (s != DLADM_STATUS_OK)
1439 				status = s;
1440 		}
1441 		goto done;
1442 	}
1443 
1444 	for (i = 0; i < proplist->al_count; i++) {
1445 		dladm_arg_info_t	*aip = &proplist->al_info[i];
1446 		char		**val;
1447 		uint_t		count;
1448 		dladm_status_t	s;
1449 
1450 		if (reset) {
1451 			val = NULL;
1452 			count = 0;
1453 		} else {
1454 			val = aip->ai_val;
1455 			count = aip->ai_count;
1456 			if (count == 0) {
1457 				warn("no value specified for '%s'",
1458 				    aip->ai_name);
1459 				status = DLADM_STATUS_BADARG;
1460 				continue;
1461 			}
1462 		}
1463 		s = dladm_set_flowprop(handle, flow, aip->ai_name, val, count,
1464 		    DLADM_OPT_ACTIVE, NULL);
1465 		if (s == DLADM_STATUS_OK) {
1466 			if (!temp) {
1467 				s = set_flowprop_persist(flow,
1468 				    aip->ai_name, val, count, reset);
1469 				if (s != DLADM_STATUS_OK)
1470 					status = s;
1471 			}
1472 			continue;
1473 		}
1474 		status = s;
1475 		switch (s) {
1476 		case DLADM_STATUS_NOTFOUND:
1477 			warn("invalid flow property '%s'", aip->ai_name);
1478 			break;
1479 		case DLADM_STATUS_BADVAL: {
1480 			int		j;
1481 			char		*ptr, *lim;
1482 			char		**propvals = NULL;
1483 			uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
1484 
1485 			ptr = malloc((sizeof (char *) +
1486 			    DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT +
1487 			    MAX_PROP_LINE);
1488 
1489 			if (ptr == NULL)
1490 				die("insufficient memory");
1491 			propvals = (char **)(void *)ptr;
1492 
1493 			for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) {
1494 				propvals[j] = ptr + sizeof (char *) *
1495 				    DLADM_MAX_PROP_VALCNT +
1496 				    j * DLADM_PROP_VAL_MAX;
1497 			}
1498 			s = dladm_get_flowprop(handle, flow,
1499 			    DLADM_PROP_VAL_MODIFIABLE, aip->ai_name, propvals,
1500 			    &valcnt);
1501 
1502 			ptr = errmsg;
1503 			lim = ptr + DLADM_STRSIZE;
1504 			*ptr = '\0';
1505 			for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) {
1506 				ptr += snprintf(ptr, lim - ptr, "%s,",
1507 				    propvals[j]);
1508 				if (ptr >= lim)
1509 					break;
1510 			}
1511 			if (ptr > errmsg) {
1512 				*(ptr - 1) = '\0';
1513 				warn("flow property '%s' must be one of: %s",
1514 				    aip->ai_name, errmsg);
1515 			} else
1516 				warn("%s is an invalid value for "
1517 				    "flow property %s", *val, aip->ai_name);
1518 			free(propvals);
1519 			break;
1520 		}
1521 		default:
1522 			if (reset) {
1523 				warn_dlerr(status, "cannot reset flow property "
1524 				    "'%s' on '%s'", aip->ai_name, flow);
1525 			} else {
1526 				warn_dlerr(status, "cannot set flow property "
1527 				    "'%s' on '%s'", aip->ai_name, flow);
1528 			}
1529 			break;
1530 		}
1531 	}
1532 done:
1533 	dladm_free_props(proplist);
1534 	if (status != DLADM_STATUS_OK) {
1535 		dladm_close(handle);
1536 		exit(EXIT_FAILURE);
1537 	}
1538 }
1539 
1540 static void
1541 do_set_flowprop(int argc, char **argv)
1542 {
1543 	set_flowprop(argc, argv, B_FALSE);
1544 }
1545 
1546 static void
1547 do_reset_flowprop(int argc, char **argv)
1548 {
1549 	set_flowprop(argc, argv, B_TRUE);
1550 }
1551 
1552 static void
1553 warn(const char *format, ...)
1554 {
1555 	va_list alist;
1556 
1557 	format = gettext(format);
1558 	(void) fprintf(stderr, "%s: warning: ", progname);
1559 
1560 	va_start(alist, format);
1561 	(void) vfprintf(stderr, format, alist);
1562 	va_end(alist);
1563 
1564 	(void) putchar('\n');
1565 }
1566 
1567 /* PRINTFLIKE2 */
1568 static void
1569 warn_dlerr(dladm_status_t err, const char *format, ...)
1570 {
1571 	va_list alist;
1572 	char    errmsg[DLADM_STRSIZE];
1573 
1574 	format = gettext(format);
1575 	(void) fprintf(stderr, gettext("%s: warning: "), progname);
1576 
1577 	va_start(alist, format);
1578 	(void) vfprintf(stderr, format, alist);
1579 	va_end(alist);
1580 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
1581 }
1582 
1583 /* PRINTFLIKE1 */
1584 static void
1585 die(const char *format, ...)
1586 {
1587 	va_list alist;
1588 
1589 	format = gettext(format);
1590 	(void) fprintf(stderr, "%s: ", progname);
1591 
1592 	va_start(alist, format);
1593 	(void) vfprintf(stderr, format, alist);
1594 	va_end(alist);
1595 
1596 	(void) putchar('\n');
1597 
1598 	/* close dladm handle if it was opened */
1599 	if (handle != NULL)
1600 		dladm_close(handle);
1601 
1602 	exit(EXIT_FAILURE);
1603 }
1604 
1605 static void
1606 die_optdup(int opt)
1607 {
1608 	die("the option -%c cannot be specified more than once", opt);
1609 }
1610 
1611 static void
1612 die_opterr(int opt, int opterr)
1613 {
1614 	switch (opterr) {
1615 	case ':':
1616 		die("option '-%c' requires a value", opt);
1617 		break;
1618 	case '?':
1619 	default:
1620 		die("unrecognized option '-%c'", opt);
1621 		break;
1622 	}
1623 }
1624 
1625 /* PRINTFLIKE2 */
1626 static void
1627 die_dlerr(dladm_status_t err, const char *format, ...)
1628 {
1629 	va_list alist;
1630 	char	errmsg[DLADM_STRSIZE];
1631 
1632 	format = gettext(format);
1633 	(void) fprintf(stderr, "%s: ", progname);
1634 
1635 	va_start(alist, format);
1636 	(void) vfprintf(stderr, format, alist);
1637 	va_end(alist);
1638 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
1639 
1640 	/* close dladm handle if it was opened */
1641 	if (handle != NULL)
1642 		dladm_close(handle);
1643 
1644 	exit(EXIT_FAILURE);
1645 }
1646 
1647 static void
1648 print_flowprop(const char *flowname, show_flowprop_state_t *statep,
1649     const char *propname, dladm_prop_type_t type,
1650     const char *format, char **pptr)
1651 {
1652 	int		i;
1653 	char		*ptr, *lim;
1654 	char		buf[DLADM_STRSIZE];
1655 	char		*unknown = "--", *notsup = "";
1656 	char		**propvals = statep->fs_propvals;
1657 	uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
1658 	dladm_status_t	status;
1659 
1660 	status = dladm_get_flowprop(handle, flowname, type, propname, propvals,
1661 	    &valcnt);
1662 	if (status != DLADM_STATUS_OK) {
1663 		if (status == DLADM_STATUS_TEMPONLY) {
1664 			if (type == DLADM_PROP_VAL_MODIFIABLE &&
1665 			    statep->fs_persist) {
1666 				valcnt = 1;
1667 				propvals = &unknown;
1668 			} else {
1669 				statep->fs_status = status;
1670 				statep->fs_retstatus = status;
1671 				return;
1672 			}
1673 		} else if (status == DLADM_STATUS_NOTSUP ||
1674 		    statep->fs_persist) {
1675 			valcnt = 1;
1676 			if (type == DLADM_PROP_VAL_CURRENT)
1677 				propvals = &unknown;
1678 			else
1679 				propvals = &notsup;
1680 		} else {
1681 			if ((statep->fs_proplist != NULL) &&
1682 			    statep->fs_status == DLADM_STATUS_OK) {
1683 				warn("invalid flow property '%s'", propname);
1684 			}
1685 			statep->fs_status = status;
1686 			statep->fs_retstatus = status;
1687 			return;
1688 		}
1689 	}
1690 
1691 	statep->fs_status = DLADM_STATUS_OK;
1692 
1693 	ptr = buf;
1694 	lim = buf + DLADM_STRSIZE;
1695 	for (i = 0; i < valcnt; i++) {
1696 		if (propvals[i][0] == '\0' && !statep->fs_parseable)
1697 			ptr += snprintf(ptr, lim - ptr, STR_UNDEF_VAL",");
1698 		else
1699 			ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]);
1700 		if (ptr >= lim)
1701 			break;
1702 	}
1703 	if (valcnt > 0)
1704 		buf[strlen(buf) - 1] = '\0';
1705 
1706 	lim = statep->fs_line + MAX_PROP_LINE;
1707 	if (statep->fs_parseable) {
1708 		*pptr += snprintf(*pptr, lim - *pptr,
1709 		    "%s", buf);
1710 	} else {
1711 		*pptr += snprintf(*pptr, lim - *pptr, format, buf);
1712 	}
1713 }
1714 
1715 static char *
1716 flowprop_callback(print_field_t *pf, void *fs_arg)
1717 {
1718 	flowprop_args_t		*arg = fs_arg;
1719 	char 			*propname = arg->fs_propname;
1720 	show_flowprop_state_t	*statep = arg->fs_state;
1721 	char			*ptr = statep->fs_line;
1722 	char			*lim = ptr + MAX_PROP_LINE;
1723 	char			*flowname = arg->fs_flowname;
1724 
1725 	switch (pf->pf_index) {
1726 	case FLOWPROP_FLOW:
1727 		(void) snprintf(ptr, lim - ptr, "%s", statep->fs_flow);
1728 		break;
1729 	case FLOWPROP_PROPERTY:
1730 		(void) snprintf(ptr, lim - ptr, "%s", propname);
1731 		break;
1732 	case FLOWPROP_VALUE:
1733 		print_flowprop(flowname, statep, propname,
1734 		    statep->fs_persist ? DLADM_PROP_VAL_PERSISTENT :
1735 		    DLADM_PROP_VAL_CURRENT, "%s", &ptr);
1736 		/*
1737 		 * If we failed to query the flow property, for example, query
1738 		 * the persistent value of a non-persistable flow property,
1739 		 * simply skip the output.
1740 		 */
1741 		if (statep->fs_status != DLADM_STATUS_OK)
1742 			goto skip;
1743 		ptr = statep->fs_line;
1744 		break;
1745 	case FLOWPROP_DEFAULT:
1746 		print_flowprop(flowname, statep, propname,
1747 		    DLADM_PROP_VAL_DEFAULT, "%s", &ptr);
1748 		if (statep->fs_status != DLADM_STATUS_OK)
1749 			goto skip;
1750 		ptr = statep->fs_line;
1751 		break;
1752 	case FLOWPROP_POSSIBLE:
1753 		print_flowprop(flowname, statep, propname,
1754 		    DLADM_PROP_VAL_MODIFIABLE, "%s ", &ptr);
1755 		if (statep->fs_status != DLADM_STATUS_OK)
1756 			goto skip;
1757 		ptr = statep->fs_line;
1758 		break;
1759 	default:
1760 		die("invalid input");
1761 		break;
1762 	}
1763 	return (ptr);
1764 skip:
1765 	if (statep->fs_status != DLADM_STATUS_OK)
1766 		return (NULL);
1767 	else
1768 		return ("");
1769 }
1770 
1771 static int
1772 show_one_flowprop(void *arg, const char *propname)
1773 {
1774 	show_flowprop_state_t	*statep = arg;
1775 	flowprop_args_t		fs_arg;
1776 
1777 	bzero(&fs_arg, sizeof (fs_arg));
1778 	fs_arg.fs_state = statep;
1779 	fs_arg.fs_propname = (char *)propname;
1780 	fs_arg.fs_flowname = (char *)statep->fs_flow;
1781 
1782 	if (statep->fs_header) {
1783 		statep->fs_header = B_FALSE;
1784 		if (!statep ->fs_parseable)
1785 			print_header(&statep->fs_print);
1786 	}
1787 	flowadm_print_output(&statep->fs_print, statep->fs_parseable,
1788 	    flowprop_callback, (void *)&fs_arg);
1789 
1790 	return (DLADM_WALK_CONTINUE);
1791 }
1792 
1793 /* Walker function called by dladm_walk_flow to display flow properties */
1794 static int
1795 show_flowprop(dladm_flow_attr_t *attr, void *arg)
1796 {
1797 	show_flowprop_one_flow(arg, attr->fa_flowname);
1798 	return (DLADM_WALK_CONTINUE);
1799 }
1800 
1801 /*
1802  * Wrapper of dladm_walk_flow(show_walk_fn,...) to make it
1803  * usable to dladm_walk_datalink_id()
1804  */
1805 static int
1806 show_flowprop_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg)
1807 {
1808 	char	name[MAXLINKNAMELEN];
1809 
1810 	if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, name,
1811 	    sizeof (name)) != DLADM_STATUS_OK)
1812 		return (DLADM_WALK_TERMINATE);
1813 
1814 	(void) dladm_walk_flow(show_flowprop, dh, linkid, arg, B_FALSE);
1815 
1816 	return (DLADM_WALK_CONTINUE);
1817 }
1818 
1819 static void
1820 do_show_flowprop(int argc, char **argv)
1821 {
1822 	int			option;
1823 	boolean_t		o_arg = B_FALSE;
1824 	dladm_arg_list_t	*proplist = NULL;
1825 	show_flowprop_state_t	state;
1826 	char			*fields_str = NULL;
1827 	print_field_t		**fields;
1828 	uint_t			nfields;
1829 	char			*all_fields =
1830 	    "flow,property,value,default,possible";
1831 
1832 	fields_str = all_fields;
1833 	opterr = 0;
1834 	state.fs_propvals = NULL;
1835 	state.fs_line = NULL;
1836 	state.fs_parseable = B_FALSE;
1837 	state.fs_persist = B_FALSE;
1838 	state.fs_header = B_TRUE;
1839 	state.fs_retstatus = DLADM_STATUS_OK;
1840 	state.fs_linkid = DATALINK_INVALID_LINKID;
1841 	state.fs_flow = NULL;
1842 
1843 	while ((option = getopt_long(argc, argv, ":p:cPl:o:",
1844 	    prop_longopts, NULL)) != -1) {
1845 		switch (option) {
1846 		case 'p':
1847 			if (dladm_parse_flow_props(optarg, &proplist, B_TRUE)
1848 			    != DLADM_STATUS_OK)
1849 				die("invalid flow properties specified");
1850 			break;
1851 		case 'c':
1852 			state.fs_parseable = B_TRUE;
1853 			break;
1854 		case 'P':
1855 			state.fs_persist = B_TRUE;
1856 			break;
1857 		case 'l':
1858 			if (dladm_name2info(handle, optarg, &state.fs_linkid,
1859 			    NULL, NULL, NULL) != DLADM_STATUS_OK)
1860 				die("invalid link '%s'", optarg);
1861 			break;
1862 		case 'o':
1863 			o_arg = B_TRUE;
1864 			if (strcasecmp(optarg, "all") == 0)
1865 				fields_str = all_fields;
1866 			else
1867 				fields_str = optarg;
1868 			break;
1869 		default:
1870 			die_opterr(optopt, option);
1871 			break;
1872 		}
1873 	}
1874 
1875 	if (state.fs_parseable && !o_arg)
1876 		die("-p requires -o");
1877 
1878 	if (state.fs_parseable && strcasecmp(fields_str, "all") == 0)
1879 		die("\"-o all\" is invalid with -p");
1880 
1881 	if (optind == (argc - 1)) {
1882 		if (strlen(argv[optind]) >= MAXFLOWNAMELEN)
1883 			die("flow name too long");
1884 		state.fs_flow = argv[optind];
1885 	} else if (optind != argc) {
1886 		usage();
1887 	}
1888 	bzero(&state.fs_print, sizeof (print_state_t));
1889 	state.fs_proplist = proplist;
1890 	state.fs_status = DLADM_STATUS_OK;
1891 
1892 	fields = parse_output_fields(fields_str, flowprop_fields,
1893 	    FLOWPROP_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
1894 
1895 	if (fields == NULL) {
1896 		die("invalid field(s) specified");
1897 		return;
1898 	}
1899 
1900 	state.fs_print.ps_fields = fields;
1901 	state.fs_print.ps_nfields = nfields;
1902 
1903 	/* Show properties for one flow */
1904 	if (state.fs_flow != NULL) {
1905 		show_flowprop_one_flow(&state, state.fs_flow);
1906 
1907 	/* Show properties for all flows on one link */
1908 	} else if (state.fs_linkid != DATALINK_INVALID_LINKID) {
1909 		(void) show_flowprop_onelink(handle, state.fs_linkid, &state);
1910 
1911 	/* Show properties for all flows on all links */
1912 	} else {
1913 		(void) dladm_walk_datalink_id(show_flowprop_onelink, handle,
1914 		    &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
1915 		    DLADM_OPT_ACTIVE);
1916 	}
1917 
1918 	dladm_free_props(proplist);
1919 }
1920 
1921 static void
1922 show_flowprop_one_flow(void *arg, const char *flow)
1923 {
1924 	int			i;
1925 	char			*buf;
1926 	dladm_status_t		status;
1927 	dladm_arg_list_t	*proplist = NULL;
1928 	show_flowprop_state_t	*statep = arg;
1929 	dladm_flow_attr_t	attr;
1930 	const char		*savep;
1931 
1932 	/*
1933 	 * Do not print flow props for invalid flows.
1934 	 */
1935 	if ((status = dladm_flow_info(handle, flow, &attr)) !=
1936 	    DLADM_STATUS_OK) {
1937 		die("invalid flow: '%s'", flow);
1938 	}
1939 
1940 	savep = statep->fs_flow;
1941 	statep->fs_flow = flow;
1942 
1943 	proplist = statep->fs_proplist;
1944 
1945 	buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX)
1946 	    * DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE);
1947 	if (buf == NULL)
1948 		die("insufficient memory");
1949 
1950 	statep->fs_propvals = (char **)(void *)buf;
1951 	for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
1952 		statep->fs_propvals[i] = buf +
1953 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
1954 		    i * DLADM_PROP_VAL_MAX;
1955 	}
1956 	statep->fs_line = buf +
1957 	    (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT;
1958 
1959 	/* show only specified flow properties */
1960 	if (proplist != NULL) {
1961 		for (i = 0; i < proplist->al_count; i++) {
1962 			if (show_one_flowprop(statep,
1963 			    proplist->al_info[i].ai_name) != DLADM_STATUS_OK)
1964 				break;
1965 		}
1966 
1967 	/* show all flow properties */
1968 	} else {
1969 		status = dladm_walk_flowprop(show_one_flowprop, flow, statep);
1970 		if (status != DLADM_STATUS_OK)
1971 			die_dlerr(status, "show-flowprop");
1972 	}
1973 	free(buf);
1974 	statep->fs_flow = savep;
1975 }
1976 
1977 typedef struct {
1978 	char	*s_buf;
1979 	char	**s_fields;	/* array of pointer to the fields in s_buf */
1980 	uint_t	s_nfields;	/* the number of fields in s_buf */
1981 } split_t;
1982 
1983 /*
1984  * Free the split_t structure pointed to by `sp'.
1985  */
1986 static void
1987 splitfree(split_t *sp)
1988 {
1989 	free(sp->s_buf);
1990 	free(sp->s_fields);
1991 	free(sp);
1992 }
1993 
1994 /*
1995  * Split `str' into at most `maxfields' fields, each field at most `maxlen' in
1996  * length.  Return a pointer to a split_t containing the split fields, or NULL
1997  * on failure.
1998  */
1999 static split_t *
2000 split(const char *str, uint_t maxfields, uint_t maxlen)
2001 {
2002 	char	*field, *token, *lasts = NULL;
2003 	split_t	*sp;
2004 
2005 	if (*str == '\0' || maxfields == 0 || maxlen == 0)
2006 		return (NULL);
2007 
2008 	sp = calloc(sizeof (split_t), 1);
2009 	if (sp == NULL)
2010 		return (NULL);
2011 
2012 	sp->s_buf = strdup(str);
2013 	sp->s_fields = malloc(sizeof (char *) * maxfields);
2014 	if (sp->s_buf == NULL || sp->s_fields == NULL)
2015 		goto fail;
2016 
2017 	token = sp->s_buf;
2018 	while ((field = strtok_r(token, ",", &lasts)) != NULL) {
2019 		if (sp->s_nfields == maxfields || strlen(field) > maxlen)
2020 			goto fail;
2021 		token = NULL;
2022 		sp->s_fields[sp->s_nfields++] = field;
2023 	}
2024 	return (sp);
2025 fail:
2026 	splitfree(sp);
2027 	return (NULL);
2028 }
2029 
2030 static print_field_t **
2031 parse_output_fields(char *str, print_field_t *template, int max_fields,
2032     uint_t cmdtype, uint_t *countp)
2033 {
2034 	split_t		*sp;
2035 	boolean_t	good_match = B_FALSE;
2036 	uint_t		i, j;
2037 	print_field_t	**pf = NULL;
2038 
2039 	sp = split(str, max_fields, MAX_FIELD_LEN);
2040 
2041 	if (sp == NULL)
2042 		return (NULL);
2043 
2044 	pf = malloc(sp->s_nfields * sizeof (print_field_t *));
2045 	if (pf == NULL)
2046 		goto fail;
2047 
2048 	for (i = 0; i < sp->s_nfields; i++) {
2049 		for (j = 0; j < max_fields; j++) {
2050 			if (strcasecmp(sp->s_fields[i],
2051 			    template[j].pf_name) == 0) {
2052 				good_match = template[j]. pf_cmdtype & cmdtype;
2053 				break;
2054 			}
2055 		}
2056 		if (!good_match)
2057 			goto fail;
2058 
2059 		good_match = B_FALSE;
2060 		pf[i] = &template[j];
2061 	}
2062 	*countp = i;
2063 	splitfree(sp);
2064 	return (pf);
2065 fail:
2066 	free(pf);
2067 	splitfree(sp);
2068 	return (NULL);
2069 }
2070 
2071 static void
2072 flowadm_print_output(print_state_t *statep, boolean_t parseable,
2073     print_callback_t fn, void *arg)
2074 {
2075 	int i;
2076 	char *value;
2077 	print_field_t **pf;
2078 
2079 	pf = statep->ps_fields;
2080 	for (i = 0; i < statep->ps_nfields; i++) {
2081 		statep->ps_lastfield = (i + 1 == statep->ps_nfields);
2082 		value = (*fn)(pf[i], arg);
2083 		if (value != NULL)
2084 			print_field(statep, pf[i], value, parseable);
2085 	}
2086 	(void) putchar('\n');
2087 }
2088 
2089 static void
2090 print_header(print_state_t *ps)
2091 {
2092 	int i;
2093 	print_field_t **pf;
2094 
2095 	pf = ps->ps_fields;
2096 	for (i = 0; i < ps->ps_nfields; i++) {
2097 		ps->ps_lastfield = (i + 1 == ps->ps_nfields);
2098 		print_field(ps, pf[i], pf[i]->pf_header, B_FALSE);
2099 	}
2100 	(void) putchar('\n');
2101 }
2102 
2103 static void
2104 print_field(print_state_t *statep, print_field_t *pfp, const char *value,
2105     boolean_t parseable)
2106 {
2107 	uint_t	width = pfp->pf_width;
2108 	uint_t	valwidth;
2109 	uint_t	compress;
2110 
2111 	/*
2112 	 * Parsable fields are separated by ':'. If such a field contains
2113 	 * a ':' or '\', this character is prefixed by a '\'.
2114 	 */
2115 	if (parseable) {
2116 		char	c;
2117 
2118 		if (statep->ps_nfields == 1) {
2119 			(void) printf("%s", value);
2120 			return;
2121 		}
2122 		while ((c = *value++) != '\0') {
2123 			if (c == ':' || c == '\\')
2124 				(void) putchar('\\');
2125 			(void) putchar(c);
2126 		}
2127 		if (!statep->ps_lastfield)
2128 			(void) putchar(':');
2129 		return;
2130 	} else {
2131 		if (value[0] == '\0')
2132 			value = STR_UNDEF_VAL;
2133 		if (statep->ps_lastfield) {
2134 			(void) printf("%s", value);
2135 			statep->ps_overflow = 0;
2136 			return;
2137 		}
2138 
2139 		valwidth = strlen(value);
2140 		if (valwidth > width) {
2141 			statep->ps_overflow += valwidth - width;
2142 		} else if (valwidth < width && statep->ps_overflow > 0) {
2143 			compress = min(statep->ps_overflow, width - valwidth);
2144 			statep->ps_overflow -= compress;
2145 			width -= compress;
2146 		}
2147 		(void) printf("%-*s", width, value);
2148 	}
2149 
2150 	if (!statep->ps_lastfield)
2151 		(void) putchar(' ');
2152 }
2153