xref: /titanic_44/usr/src/cmd/flowadm/flowadm.c (revision 8475e04352e630e4bd0f59a283286ee2475a14ce)
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 	dladm_arg_list_t	*proplist = NULL;
788 	dladm_arg_list_t	*attrlist = NULL;
789 	dladm_status_t		status;
790 
791 	while ((option = getopt_long(argc, argv, "tR:l:a:p:",
792 	    prop_longopts, NULL)) != -1) {
793 		switch (option) {
794 		case 't':
795 			t_arg = B_TRUE;
796 			break;
797 		case 'R':
798 			altroot = optarg;
799 			break;
800 		case 'l':
801 			if (strlcpy(devname, optarg,
802 			    MAXLINKNAMELEN) >= MAXLINKNAMELEN) {
803 				die("link name too long");
804 			}
805 			if (dladm_name2info(handle, devname, &linkid, NULL,
806 			    NULL, NULL) != DLADM_STATUS_OK)
807 				die("invalid link '%s'", devname);
808 			l_arg = B_TRUE;
809 			break;
810 		case 'a':
811 			if (dladm_parse_flow_attrs(optarg, &attrlist, B_FALSE)
812 			    != DLADM_STATUS_OK)
813 				die("invalid flow attribute specified");
814 			break;
815 		case 'p':
816 			if (dladm_parse_flow_props(optarg, &proplist, B_FALSE)
817 			    != DLADM_STATUS_OK)
818 				die("invalid flow property specified");
819 			break;
820 		default:
821 			die_opterr(optopt, option);
822 		}
823 	}
824 	if (!l_arg) {
825 		die("link is required");
826 	}
827 
828 	opterr = 0;
829 	index = optind;
830 
831 	if ((index != (argc - 1)) || match_attr(argv[index]) != NULL) {
832 		die("flow name is required");
833 	} else {
834 		/* get flow name; required last argument */
835 		if (strlen(argv[index]) >= MAXFLOWNAMELEN)
836 			die("flow name too long");
837 		name = argv[index];
838 	}
839 
840 	status = dladm_flow_add(handle, linkid, attrlist, proplist, name,
841 	    t_arg, altroot);
842 	if (status != DLADM_STATUS_OK)
843 		die_dlerr(status, "add flow failed");
844 
845 	dladm_free_attrs(attrlist);
846 	dladm_free_props(proplist);
847 }
848 
849 static void
850 do_remove_flow(int argc, char *argv[])
851 {
852 	char			option;
853 	char			*flowname = NULL;
854 	char			linkname[MAXLINKNAMELEN];
855 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
856 	boolean_t		l_arg = B_FALSE;
857 	remove_flow_state_t	state;
858 	dladm_status_t		status;
859 
860 	bzero(&state, sizeof (state));
861 
862 	opterr = 0;
863 	while ((option = getopt_long(argc, argv, ":tR:l:",
864 	    longopts, NULL)) != -1) {
865 		switch (option) {
866 		case 't':
867 			t_arg = B_TRUE;
868 			break;
869 		case 'R':
870 			altroot = optarg;
871 			break;
872 		case 'l':
873 			if (strlcpy(linkname, optarg,
874 			    MAXLINKNAMELEN) >= MAXLINKNAMELEN) {
875 				die("link name too long");
876 			}
877 			if (dladm_name2info(handle, linkname, &linkid, NULL,
878 			    NULL, NULL) != DLADM_STATUS_OK) {
879 				die("invalid link '%s'", linkname);
880 			}
881 			l_arg = B_TRUE;
882 			break;
883 		default:
884 			die_opterr(optopt, option);
885 			break;
886 		}
887 	}
888 
889 	/* when link not specified get flow name */
890 	if (!l_arg) {
891 		if (optind != (argc-1)) {
892 			usage();
893 		} else {
894 			if (strlen(argv[optind]) >= MAXFLOWNAMELEN)
895 				die("flow name too long");
896 			flowname = argv[optind];
897 		}
898 		status = dladm_flow_remove(handle, flowname, t_arg, altroot);
899 	} else {
900 		/* if link is specified then flow name should not be there */
901 		if (optind == argc-1)
902 			usage();
903 		/* walk the link to find flows and remove them */
904 		state.fs_tempop = t_arg;
905 		state.fs_altroot = altroot;
906 		state.fs_status = DLADM_STATUS_OK;
907 		status = dladm_walk_flow(remove_flow, handle, linkid, &state,
908 		    B_FALSE);
909 		/*
910 		 * check if dladm_walk_flow terminated early and see if the
911 		 * walker function as any status for us
912 		 */
913 		if (status == DLADM_STATUS_OK)
914 			status = state.fs_status;
915 	}
916 
917 	if (status != DLADM_STATUS_OK)
918 		die_dlerr(status, "remove flow failed");
919 }
920 
921 /*
922  * Walker function for removing a flow through dladm_walk_flow();
923  */
924 static int
925 remove_flow(dladm_flow_attr_t *attr, void *arg)
926 {
927 	remove_flow_state_t	*state = (remove_flow_state_t *)arg;
928 
929 	state->fs_status = dladm_flow_remove(handle, attr->fa_flowname,
930 	    state->fs_tempop, state->fs_altroot);
931 
932 	if (state->fs_status == DLADM_STATUS_OK)
933 		return (DLADM_WALK_CONTINUE);
934 	else
935 		return (DLADM_WALK_TERMINATE);
936 }
937 
938 static char *
939 flowadm_print_field(print_field_t *pf, void *arg)
940 {
941 	char *value;
942 
943 	value = (char *)arg + pf->pf_offset;
944 	return (value);
945 }
946 
947 /*ARGSUSED*/
948 static dladm_status_t
949 print_flow(show_flow_state_t *state, dladm_flow_attr_t *attr,
950     flow_fields_buf_t *fbuf)
951 {
952 	char		link[MAXLINKNAMELEN];
953 	dladm_status_t	status;
954 
955 	if ((status = dladm_datalink_id2info(handle, attr->fa_linkid, NULL,
956 	    NULL, NULL, link, sizeof (link))) != DLADM_STATUS_OK) {
957 		return (status);
958 	}
959 
960 	(void) snprintf(fbuf->flow_name, sizeof (fbuf->flow_name),
961 	    "%s", attr->fa_flowname);
962 	(void) snprintf(fbuf->flow_link, sizeof (fbuf->flow_link),
963 	    "%s", link);
964 
965 	(void) dladm_flow_attr_ip2str(attr, fbuf->flow_ipaddr,
966 	    sizeof (fbuf->flow_ipaddr));
967 	(void) dladm_flow_attr_proto2str(attr, fbuf->flow_proto,
968 	    sizeof (fbuf->flow_proto));
969 	(void) dladm_flow_attr_port2str(attr, fbuf->flow_port,
970 	    sizeof (fbuf->flow_port));
971 	(void) dladm_flow_attr_dsfield2str(attr, fbuf->flow_dsfield,
972 	    sizeof (fbuf->flow_dsfield));
973 
974 	return (DLADM_STATUS_OK);
975 }
976 
977 /*
978  * Walker function for showing flow attributes through dladm_walk_flow().
979  */
980 static int
981 show_flow(dladm_flow_attr_t *attr, void *arg)
982 {
983 	show_flow_state_t	*statep = arg;
984 	dladm_status_t		status;
985 	flow_fields_buf_t	fbuf;
986 
987 	/*
988 	 * first get all the flow attributes into fbuf;
989 	 */
990 	bzero(&fbuf, sizeof (fbuf));
991 	status = print_flow(statep, attr, &fbuf);
992 
993 	if (status != DLADM_STATUS_OK)
994 		goto done;
995 
996 	if (!statep->fs_parseable && !statep->fs_printheader) {
997 		print_header(&statep->fs_print);
998 		statep->fs_printheader = B_TRUE;
999 	}
1000 
1001 	flowadm_print_output(&statep->fs_print, statep->fs_parseable,
1002 	    flowadm_print_field, (void *)&fbuf);
1003 
1004 done:
1005 	statep->fs_status = status;
1006 	return (DLADM_WALK_CONTINUE);
1007 }
1008 
1009 static void
1010 show_one_flow(void *arg, const char *name)
1011 {
1012 	dladm_flow_attr_t	attr;
1013 
1014 	if (dladm_flow_info(handle, name, &attr) != DLADM_STATUS_OK)
1015 		die("invalid flow: '%s'", name);
1016 	else
1017 		(void) show_flow(&attr, arg);
1018 }
1019 
1020 /*
1021  * Wrapper of dladm_walk_flow(show_flow,...) to make it usable to
1022  * dladm_walk_datalink_id(). Used for showing flow attributes for
1023  * all flows on all links.
1024  */
1025 static int
1026 show_flows_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg)
1027 {
1028 	show_flow_state_t *state = arg;
1029 
1030 	(void) dladm_walk_flow(show_flow, dh, linkid, arg, state->fs_persist);
1031 
1032 	return (DLADM_WALK_CONTINUE);
1033 }
1034 
1035 static void
1036 get_flow_stats(const char *flowname, pktsum_t *stats)
1037 {
1038 	kstat_ctl_t	*kcp;
1039 	kstat_t		*ksp;
1040 
1041 	bzero(stats, sizeof (*stats));
1042 
1043 	if ((kcp = kstat_open()) == NULL) {
1044 		warn("kstat open operation failed");
1045 		return;
1046 	}
1047 
1048 	ksp = dladm_kstat_lookup(kcp, NULL, -1, flowname, "flow");
1049 
1050 	if (ksp != NULL)
1051 		dladm_get_stats(kcp, ksp, stats);
1052 
1053 	(void) kstat_close(kcp);
1054 }
1055 
1056 
1057 static char *
1058 print_flow_stats(print_field_t *pf, void *arg)
1059 {
1060 	flow_args_t	*fargs = arg;
1061 	pktsum_t	*diff_stats = fargs->flow_s_psum;
1062 	static char	buf[DLADM_STRSIZE];
1063 
1064 	switch (pf->pf_index) {
1065 	case FLOW_S_FLOW:
1066 		(void) snprintf(buf, sizeof (buf), "%s", fargs->flow_s_flow);
1067 		break;
1068 	case FLOW_S_IPKTS:
1069 		(void) snprintf(buf, sizeof (buf), "%llu",
1070 		    diff_stats->ipackets);
1071 		break;
1072 	case FLOW_S_RBYTES:
1073 		(void) snprintf(buf, sizeof (buf), "%llu",
1074 		    diff_stats->rbytes);
1075 		break;
1076 	case FLOW_S_IERRORS:
1077 		(void) snprintf(buf, sizeof (buf), "%u",
1078 		    diff_stats->ierrors);
1079 		break;
1080 	case FLOW_S_OPKTS:
1081 		(void) snprintf(buf, sizeof (buf), "%llu",
1082 		    diff_stats->opackets);
1083 		break;
1084 	case FLOW_S_OBYTES:
1085 		(void) snprintf(buf, sizeof (buf), "%llu",
1086 		    diff_stats->obytes);
1087 		break;
1088 	case FLOW_S_OERRORS:
1089 		(void) snprintf(buf, sizeof (buf), "%u",
1090 		    diff_stats->oerrors);
1091 		break;
1092 	default:
1093 		die("invalid input");
1094 		break;
1095 	}
1096 	return (buf);
1097 }
1098 /* ARGSUSED */
1099 static int
1100 show_flow_stats(dladm_flow_attr_t *attr, void *arg)
1101 {
1102 	show_flow_state_t	*state = (show_flow_state_t *)arg;
1103 	char			*name = attr->fa_flowname;
1104 	pktsum_t		stats, diff_stats;
1105 	flow_args_t		fargs;
1106 
1107 	if (state->fs_firstonly) {
1108 		if (state->fs_donefirst)
1109 			return (DLADM_WALK_TERMINATE);
1110 		state->fs_donefirst = B_TRUE;
1111 	} else {
1112 		bzero(&state->fs_prevstats, sizeof (state->fs_prevstats));
1113 	}
1114 
1115 	get_flow_stats(name, &stats);
1116 	dladm_stats_diff(&diff_stats, &stats, &state->fs_prevstats);
1117 
1118 	fargs.flow_s_flow = name;
1119 	fargs.flow_s_psum = &diff_stats;
1120 	flowadm_print_output(&state->fs_print, state->fs_parseable,
1121 	    print_flow_stats, &fargs);
1122 
1123 	state->fs_prevstats = stats;
1124 
1125 	return (DLADM_WALK_CONTINUE);
1126 }
1127 
1128 /*
1129  * Wrapper of dladm_walk_flow(show_flow,...) to make it usable for
1130  * dladm_walk_datalink_id(). Used for showing flow stats for
1131  * all flows on all links.
1132  */
1133 static int
1134 show_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg)
1135 {
1136 	if (dladm_walk_flow(show_flow_stats, dh, linkid, arg, B_FALSE)
1137 	    == DLADM_STATUS_OK)
1138 		return (DLADM_WALK_CONTINUE);
1139 	else
1140 		return (DLADM_WALK_TERMINATE);
1141 }
1142 
1143 /* ARGSUSED */
1144 static void
1145 flow_stats(const char *flow, datalink_id_t linkid,  uint_t interval,
1146     char *fields_str, show_flow_state_t *state)
1147 {
1148 	dladm_flow_attr_t	attr;
1149 	print_field_t		**fields;
1150 	uint_t			nfields;
1151 
1152 	fields = parse_output_fields(fields_str, flow_s_fields,
1153 	    FLOW_S_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
1154 	if (fields == NULL) {
1155 		die("invalid field(s) specified");
1156 		return;
1157 	}
1158 
1159 	state->fs_print.ps_fields = fields;
1160 	state->fs_print.ps_nfields = nfields;
1161 
1162 	if (flow != NULL &&
1163 	    dladm_flow_info(handle, flow, &attr) != DLADM_STATUS_OK)
1164 		die("invalid flow %s", flow);
1165 
1166 	/*
1167 	 * If an interval is specified, continuously show the stats
1168 	 * for only the first flow.
1169 	 */
1170 	state->fs_firstonly = (interval != 0);
1171 
1172 	if (!state->fs_parseable)
1173 		print_header(&state->fs_print);
1174 	for (;;) {
1175 		state->fs_donefirst = B_FALSE;
1176 
1177 		/* Show stats for named flow */
1178 		if (flow != NULL)  {
1179 			state->fs_flow = flow;
1180 			(void) show_flow_stats(&attr, state);
1181 
1182 		/* Show all stats on a link */
1183 		} else if (linkid != DATALINK_INVALID_LINKID) {
1184 			(void) dladm_walk_flow(show_flow_stats, handle, linkid,
1185 			    state, B_FALSE);
1186 
1187 		/* Show all stats by datalink */
1188 		} else {
1189 			(void) dladm_walk_datalink_id(show_link_flow_stats,
1190 			    handle, state, DATALINK_CLASS_ALL,
1191 			    DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
1192 		}
1193 
1194 		if (interval == 0)
1195 			break;
1196 
1197 		(void) sleep(interval);
1198 	}
1199 }
1200 
1201 static void
1202 do_show_flow(int argc, char *argv[])
1203 {
1204 	char			flowname[MAXFLOWNAMELEN];
1205 	char			linkname[MAXLINKNAMELEN];
1206 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
1207 	int			option;
1208 	boolean_t		s_arg = B_FALSE;
1209 	boolean_t		S_arg = B_FALSE;
1210 	boolean_t		i_arg = B_FALSE;
1211 	boolean_t		l_arg = B_FALSE;
1212 	boolean_t		o_arg = B_FALSE;
1213 	uint32_t		interval = 0;
1214 	char			*endp = NULL;
1215 	show_flow_state_t	state;
1216 	char			*fields_str = NULL;
1217 	print_field_t		**fields;
1218 	uint_t			nfields;
1219 	char			*all_fields =
1220 	    "flow,link,ipaddr,proto,port,dsfld";
1221 	char			*allstat_fields =
1222 	    "flow,ipackets,rbytes,ierrors,opackets,obytes,oerrors";
1223 
1224 	bzero(&state, sizeof (state));
1225 
1226 	opterr = 0;
1227 	while ((option = getopt_long(argc, argv, ":pPsSi:l:o:",
1228 	    longopts, NULL)) != -1) {
1229 		switch (option) {
1230 		case 'p':
1231 			state.fs_parseable = B_TRUE;
1232 			break;
1233 		case 'P':
1234 			state.fs_persist = B_TRUE;
1235 			break;
1236 		case 's':
1237 			if (s_arg)
1238 				die_optdup(option);
1239 
1240 			s_arg = B_TRUE;
1241 			break;
1242 		case 'S':
1243 			if (S_arg)
1244 				die_optdup(option);
1245 
1246 			S_arg = B_TRUE;
1247 			break;
1248 		case 'o':
1249 			if (o_arg)
1250 				die_optdup(option);
1251 
1252 			o_arg = B_TRUE;
1253 			fields_str = optarg;
1254 			break;
1255 		case 'i':
1256 			if (i_arg)
1257 				die_optdup(option);
1258 
1259 			i_arg = B_TRUE;
1260 
1261 			errno = 0;
1262 			interval = (int)strtol(optarg, &endp, 10);
1263 			if (errno != 0 || interval == 0 || *endp != '\0')
1264 				die("invalid interval value" " '%d'\n",
1265 				    interval);
1266 			break;
1267 		case 'l':
1268 			if (strlcpy(linkname, optarg, MAXLINKNAMELEN)
1269 			    >= MAXLINKNAMELEN)
1270 				die("link name too long\n");
1271 			if (dladm_name2info(handle, linkname, &linkid, NULL,
1272 			    NULL, NULL) != DLADM_STATUS_OK)
1273 				die("invalid link '%s'", linkname);
1274 			l_arg = B_TRUE;
1275 			break;
1276 		default:
1277 			die_opterr(optopt, option);
1278 			break;
1279 		}
1280 	}
1281 
1282 	if (i_arg && !(s_arg || S_arg))
1283 		die("the -i option can be used only with -s or -S");
1284 
1285 	if (s_arg && S_arg)
1286 		die("the -s option cannot be used with -S");
1287 
1288 	/* get flow name (optional last argument */
1289 	if (optind == (argc-1)) {
1290 		if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN)
1291 		    >= MAXFLOWNAMELEN)
1292 			die("flow name too long");
1293 		state.fs_flow = flowname;
1294 	}
1295 
1296 	if (S_arg) {
1297 		dladm_continuous(handle, linkid, state.fs_flow, interval,
1298 		    FLOW_REPORT);
1299 		return;
1300 	}
1301 
1302 	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) {
1303 		if (s_arg)
1304 			fields_str = allstat_fields;
1305 		else
1306 			fields_str = all_fields;
1307 	}
1308 
1309 	if (s_arg) {
1310 		flow_stats(state.fs_flow, linkid, interval, fields_str, &state);
1311 		return;
1312 	}
1313 
1314 	fields = parse_output_fields(fields_str, flow_fields, FLOW_MAX_FIELDS,
1315 	    CMD_TYPE_ANY, &nfields);
1316 
1317 	if (fields == NULL) {
1318 		die("invalid fields(s) specified");
1319 		return;
1320 	}
1321 
1322 	state.fs_print.ps_fields = fields;
1323 	state.fs_print.ps_nfields = nfields;
1324 
1325 	/* Show attributes of one flow */
1326 	if (state.fs_flow != NULL) {
1327 		show_one_flow(&state, state.fs_flow);
1328 
1329 	/* Show attributes of flows on one link */
1330 	} else if (l_arg) {
1331 		(void) show_flows_onelink(handle, linkid, &state);
1332 
1333 	/* Show attributes of all flows on all links */
1334 	} else {
1335 		(void) dladm_walk_datalink_id(show_flows_onelink, handle,
1336 		    &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
1337 		    DLADM_OPT_ACTIVE);
1338 	}
1339 }
1340 
1341 static dladm_status_t
1342 set_flowprop_persist(const char *flow, const char *prop_name, char **prop_val,
1343     uint_t val_cnt, boolean_t reset)
1344 {
1345 	dladm_status_t	status;
1346 	char		*errprop;
1347 
1348 	status = dladm_set_flowprop(handle, flow, prop_name, prop_val, val_cnt,
1349 	    DLADM_OPT_PERSIST, &errprop);
1350 
1351 	if (status != DLADM_STATUS_OK) {
1352 		warn_dlerr(status, "cannot persistently %s flow "
1353 		    "property '%s' on '%s'", reset? "reset": "set",
1354 		    errprop, flow);
1355 	}
1356 	return (status);
1357 }
1358 
1359 static void
1360 set_flowprop(int argc, char **argv, boolean_t reset)
1361 {
1362 	int		i, option;
1363 	char		errmsg[DLADM_STRSIZE];
1364 	const char	*flow = NULL;
1365 	dladm_arg_list_t	*proplist = NULL;
1366 	boolean_t	temp = B_FALSE;
1367 	dladm_status_t	status = DLADM_STATUS_OK;
1368 
1369 	opterr = 0;
1370 	while ((option = getopt_long(argc, argv, ":p:R:t",
1371 	    prop_longopts, NULL)) != -1) {
1372 		switch (option) {
1373 		case 'p':
1374 			if (dladm_parse_flow_props(optarg, &proplist, reset)
1375 			    != DLADM_STATUS_OK)
1376 				die("invalid flow property specified");
1377 			break;
1378 		case 't':
1379 			temp = B_TRUE;
1380 			break;
1381 		case 'R':
1382 			status = dladm_set_rootdir(optarg);
1383 			if (status != DLADM_STATUS_OK) {
1384 				die_dlerr(status, "invalid directory "
1385 				    "specified");
1386 			}
1387 			break;
1388 		default:
1389 			die_opterr(optopt, option);
1390 			break;
1391 		}
1392 	}
1393 
1394 	if (optind == (argc - 1)) {
1395 		if (strlen(argv[optind]) >= MAXFLOWNAMELEN)
1396 			die("flow name too long");
1397 		flow = argv[optind];
1398 	} else if (optind != argc) {
1399 		usage();
1400 	}
1401 	if (flow == NULL)
1402 		die("flow name must be specified");
1403 
1404 	if (proplist == NULL) {
1405 		char *errprop;
1406 
1407 		if (!reset)
1408 			die("flow property must be specified");
1409 
1410 		status = dladm_set_flowprop(handle, flow, NULL, NULL, 0,
1411 		    DLADM_OPT_ACTIVE, &errprop);
1412 		if (status != DLADM_STATUS_OK) {
1413 			warn_dlerr(status, "cannot reset flow property '%s' "
1414 			    "on '%s'", errprop, flow);
1415 		}
1416 		if (!temp) {
1417 			dladm_status_t	s;
1418 
1419 			s = set_flowprop_persist(flow, NULL, NULL, 0, reset);
1420 			if (s != DLADM_STATUS_OK)
1421 				status = s;
1422 		}
1423 		goto done;
1424 	}
1425 
1426 	for (i = 0; i < proplist->al_count; i++) {
1427 		dladm_arg_info_t	*aip = &proplist->al_info[i];
1428 		char		**val;
1429 		uint_t		count;
1430 		dladm_status_t	s;
1431 
1432 		if (reset) {
1433 			val = NULL;
1434 			count = 0;
1435 		} else {
1436 			val = aip->ai_val;
1437 			count = aip->ai_count;
1438 			if (count == 0) {
1439 				warn("no value specified for '%s'",
1440 				    aip->ai_name);
1441 				status = DLADM_STATUS_BADARG;
1442 				continue;
1443 			}
1444 		}
1445 		s = dladm_set_flowprop(handle, flow, aip->ai_name, val, count,
1446 		    DLADM_OPT_ACTIVE, NULL);
1447 		if (s == DLADM_STATUS_OK) {
1448 			if (!temp) {
1449 				s = set_flowprop_persist(flow,
1450 				    aip->ai_name, val, count, reset);
1451 				if (s != DLADM_STATUS_OK)
1452 					status = s;
1453 			}
1454 			continue;
1455 		}
1456 		status = s;
1457 		switch (s) {
1458 		case DLADM_STATUS_NOTFOUND:
1459 			warn("invalid flow property '%s'", aip->ai_name);
1460 			break;
1461 		case DLADM_STATUS_BADVAL: {
1462 			int		j;
1463 			char		*ptr, *lim;
1464 			char		**propvals = NULL;
1465 			uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
1466 
1467 			ptr = malloc((sizeof (char *) +
1468 			    DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT +
1469 			    MAX_PROP_LINE);
1470 
1471 			if (ptr == NULL)
1472 				die("insufficient memory");
1473 			propvals = (char **)(void *)ptr;
1474 
1475 			for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) {
1476 				propvals[j] = ptr + sizeof (char *) *
1477 				    DLADM_MAX_PROP_VALCNT +
1478 				    j * DLADM_PROP_VAL_MAX;
1479 			}
1480 			s = dladm_get_flowprop(handle, flow,
1481 			    DLADM_PROP_VAL_MODIFIABLE, aip->ai_name, propvals,
1482 			    &valcnt);
1483 
1484 			ptr = errmsg;
1485 			lim = ptr + DLADM_STRSIZE;
1486 			*ptr = '\0';
1487 			for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) {
1488 				ptr += snprintf(ptr, lim - ptr, "%s,",
1489 				    propvals[j]);
1490 				if (ptr >= lim)
1491 					break;
1492 			}
1493 			if (ptr > errmsg) {
1494 				*(ptr - 1) = '\0';
1495 				warn("flow property '%s' must be one of: %s",
1496 				    aip->ai_name, errmsg);
1497 			} else
1498 				warn("%s is an invalid value for "
1499 				    "flow property %s", *val, aip->ai_name);
1500 			free(propvals);
1501 			break;
1502 		}
1503 		default:
1504 			if (reset) {
1505 				warn_dlerr(status, "cannot reset flow property "
1506 				    "'%s' on '%s'", aip->ai_name, flow);
1507 			} else {
1508 				warn_dlerr(status, "cannot set flow property "
1509 				    "'%s' on '%s'", aip->ai_name, flow);
1510 			}
1511 			break;
1512 		}
1513 	}
1514 done:
1515 	dladm_free_props(proplist);
1516 	if (status != DLADM_STATUS_OK) {
1517 		dladm_close(handle);
1518 		exit(EXIT_FAILURE);
1519 	}
1520 }
1521 
1522 static void
1523 do_set_flowprop(int argc, char **argv)
1524 {
1525 	set_flowprop(argc, argv, B_FALSE);
1526 }
1527 
1528 static void
1529 do_reset_flowprop(int argc, char **argv)
1530 {
1531 	set_flowprop(argc, argv, B_TRUE);
1532 }
1533 
1534 static void
1535 warn(const char *format, ...)
1536 {
1537 	va_list alist;
1538 
1539 	format = gettext(format);
1540 	(void) fprintf(stderr, "%s: warning: ", progname);
1541 
1542 	va_start(alist, format);
1543 	(void) vfprintf(stderr, format, alist);
1544 	va_end(alist);
1545 
1546 	(void) putchar('\n');
1547 }
1548 
1549 /* PRINTFLIKE2 */
1550 static void
1551 warn_dlerr(dladm_status_t err, const char *format, ...)
1552 {
1553 	va_list alist;
1554 	char    errmsg[DLADM_STRSIZE];
1555 
1556 	format = gettext(format);
1557 	(void) fprintf(stderr, gettext("%s: warning: "), progname);
1558 
1559 	va_start(alist, format);
1560 	(void) vfprintf(stderr, format, alist);
1561 	va_end(alist);
1562 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
1563 }
1564 
1565 /* PRINTFLIKE1 */
1566 static void
1567 die(const char *format, ...)
1568 {
1569 	va_list alist;
1570 
1571 	format = gettext(format);
1572 	(void) fprintf(stderr, "%s: ", progname);
1573 
1574 	va_start(alist, format);
1575 	(void) vfprintf(stderr, format, alist);
1576 	va_end(alist);
1577 
1578 	(void) putchar('\n');
1579 
1580 	/* close dladm handle if it was opened */
1581 	if (handle != NULL)
1582 		dladm_close(handle);
1583 
1584 	exit(EXIT_FAILURE);
1585 }
1586 
1587 static void
1588 die_optdup(int opt)
1589 {
1590 	die("the option -%c cannot be specified more than once", opt);
1591 }
1592 
1593 static void
1594 die_opterr(int opt, int opterr)
1595 {
1596 	switch (opterr) {
1597 	case ':':
1598 		die("option '-%c' requires a value", opt);
1599 		break;
1600 	case '?':
1601 	default:
1602 		die("unrecognized option '-%c'", opt);
1603 		break;
1604 	}
1605 }
1606 
1607 /* PRINTFLIKE2 */
1608 static void
1609 die_dlerr(dladm_status_t err, const char *format, ...)
1610 {
1611 	va_list alist;
1612 	char	errmsg[DLADM_STRSIZE];
1613 
1614 	format = gettext(format);
1615 	(void) fprintf(stderr, "%s: ", progname);
1616 
1617 	va_start(alist, format);
1618 	(void) vfprintf(stderr, format, alist);
1619 	va_end(alist);
1620 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
1621 
1622 	/* close dladm handle if it was opened */
1623 	if (handle != NULL)
1624 		dladm_close(handle);
1625 
1626 	exit(EXIT_FAILURE);
1627 }
1628 
1629 static void
1630 print_flowprop(const char *flowname, show_flowprop_state_t *statep,
1631     const char *propname, dladm_prop_type_t type,
1632     const char *format, char **pptr)
1633 {
1634 	int		i;
1635 	char		*ptr, *lim;
1636 	char		buf[DLADM_STRSIZE];
1637 	char		*unknown = "--", *notsup = "";
1638 	char		**propvals = statep->fs_propvals;
1639 	uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
1640 	dladm_status_t	status;
1641 
1642 	status = dladm_get_flowprop(handle, flowname, type, propname, propvals,
1643 	    &valcnt);
1644 	if (status != DLADM_STATUS_OK) {
1645 		if (status == DLADM_STATUS_TEMPONLY) {
1646 			if (type == DLADM_PROP_VAL_MODIFIABLE &&
1647 			    statep->fs_persist) {
1648 				valcnt = 1;
1649 				propvals = &unknown;
1650 			} else {
1651 				statep->fs_status = status;
1652 				statep->fs_retstatus = status;
1653 				return;
1654 			}
1655 		} else if (status == DLADM_STATUS_NOTSUP ||
1656 		    statep->fs_persist) {
1657 			valcnt = 1;
1658 			if (type == DLADM_PROP_VAL_CURRENT)
1659 				propvals = &unknown;
1660 			else
1661 				propvals = &notsup;
1662 		} else {
1663 			if ((statep->fs_proplist != NULL) &&
1664 			    statep->fs_status == DLADM_STATUS_OK) {
1665 				warn("invalid flow property '%s'", propname);
1666 			}
1667 			statep->fs_status = status;
1668 			statep->fs_retstatus = status;
1669 			return;
1670 		}
1671 	}
1672 
1673 	statep->fs_status = DLADM_STATUS_OK;
1674 
1675 	ptr = buf;
1676 	lim = buf + DLADM_STRSIZE;
1677 	for (i = 0; i < valcnt; i++) {
1678 		if (propvals[i][0] == '\0' && !statep->fs_parseable)
1679 			ptr += snprintf(ptr, lim - ptr, STR_UNDEF_VAL",");
1680 		else
1681 			ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]);
1682 		if (ptr >= lim)
1683 			break;
1684 	}
1685 	if (valcnt > 0)
1686 		buf[strlen(buf) - 1] = '\0';
1687 
1688 	lim = statep->fs_line + MAX_PROP_LINE;
1689 	if (statep->fs_parseable) {
1690 		*pptr += snprintf(*pptr, lim - *pptr,
1691 		    "%s", buf);
1692 	} else {
1693 		*pptr += snprintf(*pptr, lim - *pptr, format, buf);
1694 	}
1695 }
1696 
1697 static char *
1698 flowprop_callback(print_field_t *pf, void *fs_arg)
1699 {
1700 	flowprop_args_t		*arg = fs_arg;
1701 	char 			*propname = arg->fs_propname;
1702 	show_flowprop_state_t	*statep = arg->fs_state;
1703 	char			*ptr = statep->fs_line;
1704 	char			*lim = ptr + MAX_PROP_LINE;
1705 	char			*flowname = arg->fs_flowname;
1706 
1707 	switch (pf->pf_index) {
1708 	case FLOWPROP_FLOW:
1709 		(void) snprintf(ptr, lim - ptr, "%s", statep->fs_flow);
1710 		break;
1711 	case FLOWPROP_PROPERTY:
1712 		(void) snprintf(ptr, lim - ptr, "%s", propname);
1713 		break;
1714 	case FLOWPROP_VALUE:
1715 		print_flowprop(flowname, statep, propname,
1716 		    statep->fs_persist ? DLADM_PROP_VAL_PERSISTENT :
1717 		    DLADM_PROP_VAL_CURRENT, "%s", &ptr);
1718 		/*
1719 		 * If we failed to query the flow property, for example, query
1720 		 * the persistent value of a non-persistable flow property,
1721 		 * simply skip the output.
1722 		 */
1723 		if (statep->fs_status != DLADM_STATUS_OK)
1724 			goto skip;
1725 		ptr = statep->fs_line;
1726 		break;
1727 	case FLOWPROP_DEFAULT:
1728 		print_flowprop(flowname, statep, propname,
1729 		    DLADM_PROP_VAL_DEFAULT, "%s", &ptr);
1730 		if (statep->fs_status != DLADM_STATUS_OK)
1731 			goto skip;
1732 		ptr = statep->fs_line;
1733 		break;
1734 	case FLOWPROP_POSSIBLE:
1735 		print_flowprop(flowname, statep, propname,
1736 		    DLADM_PROP_VAL_MODIFIABLE, "%s ", &ptr);
1737 		if (statep->fs_status != DLADM_STATUS_OK)
1738 			goto skip;
1739 		ptr = statep->fs_line;
1740 		break;
1741 	default:
1742 		die("invalid input");
1743 		break;
1744 	}
1745 	return (ptr);
1746 skip:
1747 	if (statep->fs_status != DLADM_STATUS_OK)
1748 		return (NULL);
1749 	else
1750 		return ("");
1751 }
1752 
1753 static int
1754 show_one_flowprop(void *arg, const char *propname)
1755 {
1756 	show_flowprop_state_t	*statep = arg;
1757 	flowprop_args_t		fs_arg;
1758 
1759 	bzero(&fs_arg, sizeof (fs_arg));
1760 	fs_arg.fs_state = statep;
1761 	fs_arg.fs_propname = (char *)propname;
1762 	fs_arg.fs_flowname = (char *)statep->fs_flow;
1763 
1764 	if (statep->fs_header) {
1765 		statep->fs_header = B_FALSE;
1766 		if (!statep ->fs_parseable)
1767 			print_header(&statep->fs_print);
1768 	}
1769 	flowadm_print_output(&statep->fs_print, statep->fs_parseable,
1770 	    flowprop_callback, (void *)&fs_arg);
1771 
1772 	return (DLADM_WALK_CONTINUE);
1773 }
1774 
1775 /* Walker function called by dladm_walk_flow to display flow properties */
1776 static int
1777 show_flowprop(dladm_flow_attr_t *attr, void *arg)
1778 {
1779 	show_flowprop_one_flow(arg, attr->fa_flowname);
1780 	return (DLADM_WALK_CONTINUE);
1781 }
1782 
1783 /*
1784  * Wrapper of dladm_walk_flow(show_walk_fn,...) to make it
1785  * usable to dladm_walk_datalink_id()
1786  */
1787 static int
1788 show_flowprop_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg)
1789 {
1790 	char	name[MAXLINKNAMELEN];
1791 
1792 	if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, name,
1793 	    sizeof (name)) != DLADM_STATUS_OK)
1794 		return (DLADM_WALK_TERMINATE);
1795 
1796 	(void) dladm_walk_flow(show_flowprop, dh, linkid, arg, B_FALSE);
1797 
1798 	return (DLADM_WALK_CONTINUE);
1799 }
1800 
1801 static void
1802 do_show_flowprop(int argc, char **argv)
1803 {
1804 	int			option;
1805 	boolean_t		o_arg = B_FALSE;
1806 	dladm_arg_list_t	*proplist = NULL;
1807 	show_flowprop_state_t	state;
1808 	char			*fields_str = NULL;
1809 	print_field_t		**fields;
1810 	uint_t			nfields;
1811 	char			*all_fields =
1812 	    "flow,property,value,default,possible";
1813 
1814 	fields_str = all_fields;
1815 	opterr = 0;
1816 	state.fs_propvals = NULL;
1817 	state.fs_line = NULL;
1818 	state.fs_parseable = B_FALSE;
1819 	state.fs_persist = B_FALSE;
1820 	state.fs_header = B_TRUE;
1821 	state.fs_retstatus = DLADM_STATUS_OK;
1822 	state.fs_linkid = DATALINK_INVALID_LINKID;
1823 	state.fs_flow = NULL;
1824 
1825 	while ((option = getopt_long(argc, argv, ":p:cPl:o:",
1826 	    prop_longopts, NULL)) != -1) {
1827 		switch (option) {
1828 		case 'p':
1829 			if (dladm_parse_flow_props(optarg, &proplist, B_TRUE)
1830 			    != DLADM_STATUS_OK)
1831 				die("invalid flow properties specified");
1832 			break;
1833 		case 'c':
1834 			state.fs_parseable = B_TRUE;
1835 			break;
1836 		case 'P':
1837 			state.fs_persist = B_TRUE;
1838 			break;
1839 		case 'l':
1840 			if (dladm_name2info(handle, optarg, &state.fs_linkid,
1841 			    NULL, NULL, NULL) != DLADM_STATUS_OK)
1842 				die("invalid link '%s'", optarg);
1843 			break;
1844 		case 'o':
1845 			o_arg = B_TRUE;
1846 			if (strcasecmp(optarg, "all") == 0)
1847 				fields_str = all_fields;
1848 			else
1849 				fields_str = optarg;
1850 			break;
1851 		default:
1852 			die_opterr(optopt, option);
1853 			break;
1854 		}
1855 	}
1856 
1857 	if (state.fs_parseable && !o_arg)
1858 		die("-p requires -o");
1859 
1860 	if (state.fs_parseable && strcasecmp(fields_str, "all") == 0)
1861 		die("\"-o all\" is invalid with -p");
1862 
1863 	if (optind == (argc - 1)) {
1864 		if (strlen(argv[optind]) >= MAXFLOWNAMELEN)
1865 			die("flow name too long");
1866 		state.fs_flow = argv[optind];
1867 	} else if (optind != argc) {
1868 		usage();
1869 	}
1870 	bzero(&state.fs_print, sizeof (print_state_t));
1871 	state.fs_proplist = proplist;
1872 	state.fs_status = DLADM_STATUS_OK;
1873 
1874 	fields = parse_output_fields(fields_str, flowprop_fields,
1875 	    FLOWPROP_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
1876 
1877 	if (fields == NULL) {
1878 		die("invalid field(s) specified");
1879 		return;
1880 	}
1881 
1882 	state.fs_print.ps_fields = fields;
1883 	state.fs_print.ps_nfields = nfields;
1884 
1885 	/* Show properties for one flow */
1886 	if (state.fs_flow != NULL) {
1887 		show_flowprop_one_flow(&state, state.fs_flow);
1888 
1889 	/* Show properties for all flows on one link */
1890 	} else if (state.fs_linkid != DATALINK_INVALID_LINKID) {
1891 		(void) show_flowprop_onelink(handle, state.fs_linkid, &state);
1892 
1893 	/* Show properties for all flows on all links */
1894 	} else {
1895 		(void) dladm_walk_datalink_id(show_flowprop_onelink, handle,
1896 		    &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
1897 		    DLADM_OPT_ACTIVE);
1898 	}
1899 
1900 	dladm_free_props(proplist);
1901 }
1902 
1903 static void
1904 show_flowprop_one_flow(void *arg, const char *flow)
1905 {
1906 	int			i;
1907 	char			*buf;
1908 	dladm_status_t		status;
1909 	dladm_arg_list_t	*proplist = NULL;
1910 	show_flowprop_state_t	*statep = arg;
1911 	dladm_flow_attr_t	attr;
1912 	const char		*savep;
1913 
1914 	/*
1915 	 * Do not print flow props for invalid flows.
1916 	 */
1917 	if ((status = dladm_flow_info(handle, flow, &attr)) !=
1918 	    DLADM_STATUS_OK) {
1919 		die("invalid flow: '%s'", flow);
1920 	}
1921 
1922 	savep = statep->fs_flow;
1923 	statep->fs_flow = flow;
1924 
1925 	proplist = statep->fs_proplist;
1926 
1927 	buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX)
1928 	    * DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE);
1929 	if (buf == NULL)
1930 		die("insufficient memory");
1931 
1932 	statep->fs_propvals = (char **)(void *)buf;
1933 	for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
1934 		statep->fs_propvals[i] = buf +
1935 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
1936 		    i * DLADM_PROP_VAL_MAX;
1937 	}
1938 	statep->fs_line = buf +
1939 	    (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT;
1940 
1941 	/* show only specified flow properties */
1942 	if (proplist != NULL) {
1943 		for (i = 0; i < proplist->al_count; i++) {
1944 			if (show_one_flowprop(statep,
1945 			    proplist->al_info[i].ai_name) != DLADM_STATUS_OK)
1946 				break;
1947 		}
1948 
1949 	/* show all flow properties */
1950 	} else {
1951 		status = dladm_walk_flowprop(show_one_flowprop, flow, statep);
1952 		if (status != DLADM_STATUS_OK)
1953 			die_dlerr(status, "show-flowprop");
1954 	}
1955 	free(buf);
1956 	statep->fs_flow = savep;
1957 }
1958 
1959 typedef struct {
1960 	char	*s_buf;
1961 	char	**s_fields;	/* array of pointer to the fields in s_buf */
1962 	uint_t	s_nfields;	/* the number of fields in s_buf */
1963 } split_t;
1964 
1965 /*
1966  * Free the split_t structure pointed to by `sp'.
1967  */
1968 static void
1969 splitfree(split_t *sp)
1970 {
1971 	free(sp->s_buf);
1972 	free(sp->s_fields);
1973 	free(sp);
1974 }
1975 
1976 /*
1977  * Split `str' into at most `maxfields' fields, each field at most `maxlen' in
1978  * length.  Return a pointer to a split_t containing the split fields, or NULL
1979  * on failure.
1980  */
1981 static split_t *
1982 split(const char *str, uint_t maxfields, uint_t maxlen)
1983 {
1984 	char	*field, *token, *lasts = NULL;
1985 	split_t	*sp;
1986 
1987 	if (*str == '\0' || maxfields == 0 || maxlen == 0)
1988 		return (NULL);
1989 
1990 	sp = calloc(sizeof (split_t), 1);
1991 	if (sp == NULL)
1992 		return (NULL);
1993 
1994 	sp->s_buf = strdup(str);
1995 	sp->s_fields = malloc(sizeof (char *) * maxfields);
1996 	if (sp->s_buf == NULL || sp->s_fields == NULL)
1997 		goto fail;
1998 
1999 	token = sp->s_buf;
2000 	while ((field = strtok_r(token, ",", &lasts)) != NULL) {
2001 		if (sp->s_nfields == maxfields || strlen(field) > maxlen)
2002 			goto fail;
2003 		token = NULL;
2004 		sp->s_fields[sp->s_nfields++] = field;
2005 	}
2006 	return (sp);
2007 fail:
2008 	splitfree(sp);
2009 	return (NULL);
2010 }
2011 
2012 static print_field_t **
2013 parse_output_fields(char *str, print_field_t *template, int max_fields,
2014     uint_t cmdtype, uint_t *countp)
2015 {
2016 	split_t		*sp;
2017 	boolean_t	good_match = B_FALSE;
2018 	uint_t		i, j;
2019 	print_field_t	**pf = NULL;
2020 
2021 	sp = split(str, max_fields, MAX_FIELD_LEN);
2022 
2023 	if (sp == NULL)
2024 		return (NULL);
2025 
2026 	pf = malloc(sp->s_nfields * sizeof (print_field_t *));
2027 	if (pf == NULL)
2028 		goto fail;
2029 
2030 	for (i = 0; i < sp->s_nfields; i++) {
2031 		for (j = 0; j < max_fields; j++) {
2032 			if (strcasecmp(sp->s_fields[i],
2033 			    template[j].pf_name) == 0) {
2034 				good_match = template[j]. pf_cmdtype & cmdtype;
2035 				break;
2036 			}
2037 		}
2038 		if (!good_match)
2039 			goto fail;
2040 
2041 		good_match = B_FALSE;
2042 		pf[i] = &template[j];
2043 	}
2044 	*countp = i;
2045 	splitfree(sp);
2046 	return (pf);
2047 fail:
2048 	free(pf);
2049 	splitfree(sp);
2050 	return (NULL);
2051 }
2052 
2053 static void
2054 flowadm_print_output(print_state_t *statep, boolean_t parseable,
2055     print_callback_t fn, void *arg)
2056 {
2057 	int i;
2058 	char *value;
2059 	print_field_t **pf;
2060 
2061 	pf = statep->ps_fields;
2062 	for (i = 0; i < statep->ps_nfields; i++) {
2063 		statep->ps_lastfield = (i + 1 == statep->ps_nfields);
2064 		value = (*fn)(pf[i], arg);
2065 		if (value != NULL)
2066 			print_field(statep, pf[i], value, parseable);
2067 	}
2068 	(void) putchar('\n');
2069 }
2070 
2071 static void
2072 print_header(print_state_t *ps)
2073 {
2074 	int i;
2075 	print_field_t **pf;
2076 
2077 	pf = ps->ps_fields;
2078 	for (i = 0; i < ps->ps_nfields; i++) {
2079 		ps->ps_lastfield = (i + 1 == ps->ps_nfields);
2080 		print_field(ps, pf[i], pf[i]->pf_header, B_FALSE);
2081 	}
2082 	(void) putchar('\n');
2083 }
2084 
2085 static void
2086 print_field(print_state_t *statep, print_field_t *pfp, const char *value,
2087     boolean_t parseable)
2088 {
2089 	uint_t	width = pfp->pf_width;
2090 	uint_t	valwidth;
2091 	uint_t	compress;
2092 
2093 	/*
2094 	 * Parsable fields are separated by ':'. If such a field contains
2095 	 * a ':' or '\', this character is prefixed by a '\'.
2096 	 */
2097 	if (parseable) {
2098 		char	c;
2099 
2100 		if (statep->ps_nfields == 1) {
2101 			(void) printf("%s", value);
2102 			return;
2103 		}
2104 		while ((c = *value++) != '\0') {
2105 			if (c == ':' || c == '\\')
2106 				(void) putchar('\\');
2107 			(void) putchar(c);
2108 		}
2109 		if (!statep->ps_lastfield)
2110 			(void) putchar(':');
2111 		return;
2112 	} else {
2113 		if (value[0] == '\0')
2114 			value = STR_UNDEF_VAL;
2115 		if (statep->ps_lastfield) {
2116 			(void) printf("%s", value);
2117 			statep->ps_overflow = 0;
2118 			return;
2119 		}
2120 
2121 		valwidth = strlen(value);
2122 		if (valwidth > width) {
2123 			statep->ps_overflow += valwidth - width;
2124 		} else if (valwidth < width && statep->ps_overflow > 0) {
2125 			compress = min(statep->ps_overflow, width - valwidth);
2126 			statep->ps_overflow -= compress;
2127 			width -= compress;
2128 		}
2129 		(void) printf("%-*s", width, value);
2130 	}
2131 
2132 	if (!statep->ps_lastfield)
2133 		(void) putchar(' ');
2134 }
2135