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