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