Home | History | Annotate | Download | only in flowadm
      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