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