xref: /illumos-gate/usr/src/cmd/flowadm/flowadm.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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_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 /*ARGSUSED*/
876 static int
877 remove_flow(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
878 {
879 	remove_flow_state_t	*state = (remove_flow_state_t *)arg;
880 
881 	state->fs_status = dladm_flow_remove(handle, attr->fa_flowname,
882 	    state->fs_tempop, state->fs_altroot);
883 
884 	if (state->fs_status == DLADM_STATUS_OK)
885 		return (DLADM_WALK_CONTINUE);
886 	else
887 		return (DLADM_WALK_TERMINATE);
888 }
889 
890 /*ARGSUSED*/
891 static dladm_status_t
892 print_flow(show_flow_state_t *state, dladm_flow_attr_t *attr,
893     flow_fields_buf_t *fbuf)
894 {
895 	char		link[MAXLINKNAMELEN];
896 	dladm_status_t	status;
897 
898 	if ((status = dladm_datalink_id2info(handle, attr->fa_linkid, NULL,
899 	    NULL, NULL, link, sizeof (link))) != DLADM_STATUS_OK) {
900 		return (status);
901 	}
902 
903 	(void) snprintf(fbuf->flow_name, sizeof (fbuf->flow_name),
904 	    "%s", attr->fa_flowname);
905 	(void) snprintf(fbuf->flow_link, sizeof (fbuf->flow_link),
906 	    "%s", link);
907 
908 	(void) dladm_flow_attr_ip2str(attr, fbuf->flow_ipaddr,
909 	    sizeof (fbuf->flow_ipaddr));
910 	(void) dladm_flow_attr_proto2str(attr, fbuf->flow_proto,
911 	    sizeof (fbuf->flow_proto));
912 	(void) dladm_flow_attr_port2str(attr, fbuf->flow_port,
913 	    sizeof (fbuf->flow_port));
914 	(void) dladm_flow_attr_dsfield2str(attr, fbuf->flow_dsfield,
915 	    sizeof (fbuf->flow_dsfield));
916 
917 	return (DLADM_STATUS_OK);
918 }
919 
920 /*
921  * Walker function for showing flow attributes through dladm_walk_flow().
922  */
923 /*ARGSUSED*/
924 static int
925 show_flow(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
926 {
927 	show_flow_state_t	*statep = arg;
928 	dladm_status_t		status;
929 	flow_fields_buf_t	fbuf;
930 
931 	/*
932 	 * first get all the flow attributes into fbuf;
933 	 */
934 	bzero(&fbuf, sizeof (fbuf));
935 	status = print_flow(statep, attr, &fbuf);
936 
937 	if (status != DLADM_STATUS_OK)
938 		goto done;
939 
940 	ofmt_print(statep->fs_ofmt, (void *)&fbuf);
941 
942 done:
943 	statep->fs_status = status;
944 	return (DLADM_WALK_CONTINUE);
945 }
946 
947 static void
948 show_one_flow(void *arg, const char *name)
949 {
950 	dladm_flow_attr_t	attr;
951 
952 	if (dladm_flow_info(handle, name, &attr) != DLADM_STATUS_OK)
953 		die("invalid flow: '%s'", name);
954 	else
955 		(void) show_flow(handle, &attr, arg);
956 }
957 
958 /*
959  * Wrapper of dladm_walk_flow(show_flow,...) to make it usable to
960  * dladm_walk_datalink_id(). Used for showing flow attributes for
961  * all flows on all links.
962  */
963 static int
964 show_flows_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg)
965 {
966 	show_flow_state_t *state = arg;
967 
968 	(void) dladm_walk_flow(show_flow, dh, linkid, arg, state->fs_persist);
969 
970 	return (DLADM_WALK_CONTINUE);
971 }
972 
973 static void
974 get_flow_stats(const char *flowname, pktsum_t *stats)
975 {
976 	kstat_ctl_t	*kcp;
977 	kstat_t		*ksp;
978 
979 	bzero(stats, sizeof (*stats));
980 
981 	if ((kcp = kstat_open()) == NULL) {
982 		warn("kstat open operation failed");
983 		return;
984 	}
985 
986 	ksp = dladm_kstat_lookup(kcp, NULL, -1, flowname, "flow");
987 
988 	if (ksp != NULL)
989 		dladm_get_stats(kcp, ksp, stats);
990 
991 	(void) kstat_close(kcp);
992 }
993 
994 static boolean_t
995 print_flow_stats_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
996 {
997 	flow_args_t	*fargs = of_arg->ofmt_cbarg;
998 	pktsum_t	*diff_stats = fargs->flow_s_psum;
999 
1000 	switch (of_arg->ofmt_id) {
1001 	case FLOW_S_FLOW:
1002 		(void) snprintf(buf, bufsize, "%s", fargs->flow_s_flow);
1003 		break;
1004 	case FLOW_S_IPKTS:
1005 		(void) snprintf(buf, bufsize, "%llu",
1006 		    diff_stats->ipackets);
1007 		break;
1008 	case FLOW_S_RBYTES:
1009 		(void) snprintf(buf, bufsize, "%llu",
1010 		    diff_stats->rbytes);
1011 		break;
1012 	case FLOW_S_IERRORS:
1013 		(void) snprintf(buf, bufsize, "%u",
1014 		    diff_stats->ierrors);
1015 		break;
1016 	case FLOW_S_OPKTS:
1017 		(void) snprintf(buf, bufsize, "%llu",
1018 		    diff_stats->opackets);
1019 		break;
1020 	case FLOW_S_OBYTES:
1021 		(void) snprintf(buf, bufsize, "%llu",
1022 		    diff_stats->obytes);
1023 		break;
1024 	case FLOW_S_OERRORS:
1025 		(void) snprintf(buf, bufsize, "%u",
1026 		    diff_stats->oerrors);
1027 		break;
1028 	default:
1029 		die("invalid input");
1030 		break;
1031 	}
1032 	return (B_TRUE);
1033 }
1034 
1035 /* ARGSUSED */
1036 static int
1037 show_flow_stats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
1038 {
1039 	show_flow_state_t	*state = (show_flow_state_t *)arg;
1040 	char			*name = attr->fa_flowname;
1041 	pktsum_t		stats, diff_stats;
1042 	flow_args_t		fargs;
1043 
1044 	if (state->fs_firstonly) {
1045 		if (state->fs_donefirst)
1046 			return (DLADM_WALK_TERMINATE);
1047 		state->fs_donefirst = B_TRUE;
1048 	} else {
1049 		bzero(&state->fs_prevstats, sizeof (state->fs_prevstats));
1050 	}
1051 
1052 	get_flow_stats(name, &stats);
1053 	dladm_stats_diff(&diff_stats, &stats, &state->fs_prevstats);
1054 
1055 	fargs.flow_s_flow = name;
1056 	fargs.flow_s_psum = &diff_stats;
1057 	ofmt_print(state->fs_ofmt, (void *)&fargs);
1058 	state->fs_prevstats = stats;
1059 
1060 	return (DLADM_WALK_CONTINUE);
1061 }
1062 
1063 /*
1064  * Wrapper of dladm_walk_flow(show_flow,...) to make it usable for
1065  * dladm_walk_datalink_id(). Used for showing flow stats for
1066  * all flows on all links.
1067  */
1068 static int
1069 show_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg)
1070 {
1071 	if (dladm_walk_flow(show_flow_stats, dh, linkid, arg, B_FALSE)
1072 	    == DLADM_STATUS_OK)
1073 		return (DLADM_WALK_CONTINUE);
1074 	else
1075 		return (DLADM_WALK_TERMINATE);
1076 }
1077 
1078 /* ARGSUSED */
1079 static void
1080 flow_stats(const char *flow, datalink_id_t linkid,  uint_t interval,
1081     char *fields_str, show_flow_state_t *state)
1082 {
1083 	dladm_flow_attr_t	attr;
1084 	ofmt_handle_t		ofmt;
1085 	ofmt_status_t		oferr;
1086 	uint_t			ofmtflags = 0;
1087 
1088 	oferr = ofmt_open(fields_str, flow_s_fields, ofmtflags, 0, &ofmt);
1089 	flowadm_ofmt_check(oferr, state->fs_parsable, ofmt);
1090 	state->fs_ofmt = ofmt;
1091 
1092 	if (flow != NULL &&
1093 	    dladm_flow_info(handle, flow, &attr) != DLADM_STATUS_OK)
1094 		die("invalid flow %s", flow);
1095 
1096 	/*
1097 	 * If an interval is specified, continuously show the stats
1098 	 * for only the first flow.
1099 	 */
1100 	state->fs_firstonly = (interval != 0);
1101 
1102 	for (;;) {
1103 		state->fs_donefirst = B_FALSE;
1104 
1105 		/* Show stats for named flow */
1106 		if (flow != NULL)  {
1107 			state->fs_flow = flow;
1108 			(void) show_flow_stats(handle, &attr, state);
1109 
1110 		/* Show all stats on a link */
1111 		} else if (linkid != DATALINK_INVALID_LINKID) {
1112 			(void) dladm_walk_flow(show_flow_stats, handle, linkid,
1113 			    state, B_FALSE);
1114 
1115 		/* Show all stats by datalink */
1116 		} else {
1117 			(void) dladm_walk_datalink_id(show_link_flow_stats,
1118 			    handle, state, DATALINK_CLASS_ALL,
1119 			    DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
1120 		}
1121 
1122 		if (interval == 0)
1123 			break;
1124 
1125 		(void) fflush(stdout);
1126 		(void) sleep(interval);
1127 	}
1128 	ofmt_close(ofmt);
1129 }
1130 
1131 static void
1132 do_show_flow(int argc, char *argv[])
1133 {
1134 	char			flowname[MAXFLOWNAMELEN];
1135 	char			linkname[MAXLINKNAMELEN];
1136 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
1137 	int			option;
1138 	boolean_t		s_arg = B_FALSE;
1139 	boolean_t		S_arg = B_FALSE;
1140 	boolean_t		i_arg = B_FALSE;
1141 	boolean_t		l_arg = B_FALSE;
1142 	boolean_t		o_arg = B_FALSE;
1143 	uint32_t		interval = 0;
1144 	show_flow_state_t	state;
1145 	char			*fields_str = NULL;
1146 	ofmt_handle_t		ofmt;
1147 	ofmt_status_t		oferr;
1148 	uint_t			ofmtflags = 0;
1149 
1150 	bzero(&state, sizeof (state));
1151 
1152 	opterr = 0;
1153 	while ((option = getopt_long(argc, argv, ":pPsSi:l:o:",
1154 	    longopts, NULL)) != -1) {
1155 		switch (option) {
1156 		case 'p':
1157 			state.fs_parsable = B_TRUE;
1158 			ofmtflags |= OFMT_PARSABLE;
1159 			break;
1160 		case 'P':
1161 			state.fs_persist = B_TRUE;
1162 			break;
1163 		case 's':
1164 			if (s_arg)
1165 				die_optdup(option);
1166 
1167 			s_arg = B_TRUE;
1168 			break;
1169 		case 'S':
1170 			if (S_arg)
1171 				die_optdup(option);
1172 
1173 			S_arg = B_TRUE;
1174 			break;
1175 		case 'o':
1176 			if (o_arg)
1177 				die_optdup(option);
1178 
1179 			o_arg = B_TRUE;
1180 			fields_str = optarg;
1181 			break;
1182 		case 'i':
1183 			if (i_arg)
1184 				die_optdup(option);
1185 
1186 			i_arg = B_TRUE;
1187 
1188 			if (!dladm_str2interval(optarg, &interval))
1189 				die("invalid interval value '%s'", optarg);
1190 			break;
1191 		case 'l':
1192 			if (strlcpy(linkname, optarg, MAXLINKNAMELEN)
1193 			    >= MAXLINKNAMELEN)
1194 				die("link name too long\n");
1195 			if (dladm_name2info(handle, linkname, &linkid, NULL,
1196 			    NULL, NULL) != DLADM_STATUS_OK)
1197 				die("invalid link '%s'", linkname);
1198 			l_arg = B_TRUE;
1199 			break;
1200 		default:
1201 			die_opterr(optopt, option);
1202 			break;
1203 		}
1204 	}
1205 	if (i_arg && !(s_arg || S_arg))
1206 		die("the -i option can be used only with -s or -S");
1207 
1208 	if (s_arg && S_arg)
1209 		die("the -s option cannot be used with -S");
1210 
1211 	/* get flow name (optional last argument */
1212 	if (optind == (argc-1)) {
1213 		if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN)
1214 		    >= MAXFLOWNAMELEN)
1215 			die("flow name too long");
1216 		state.fs_flow = flowname;
1217 	}
1218 
1219 	if (S_arg) {
1220 		dladm_continuous(handle, linkid, state.fs_flow, interval,
1221 		    FLOW_REPORT);
1222 		return;
1223 	}
1224 
1225 	if (s_arg) {
1226 		flow_stats(state.fs_flow, linkid, interval, fields_str, &state);
1227 		return;
1228 	}
1229 
1230 	oferr = ofmt_open(fields_str, flow_fields, ofmtflags, 0, &ofmt);
1231 	flowadm_ofmt_check(oferr, state.fs_parsable, ofmt);
1232 	state.fs_ofmt = ofmt;
1233 
1234 	/* Show attributes of one flow */
1235 	if (state.fs_flow != NULL) {
1236 		show_one_flow(&state, state.fs_flow);
1237 
1238 	/* Show attributes of flows on one link */
1239 	} else if (l_arg) {
1240 		(void) show_flows_onelink(handle, linkid, &state);
1241 
1242 	/* Show attributes of all flows on all links */
1243 	} else {
1244 		(void) dladm_walk_datalink_id(show_flows_onelink, handle,
1245 		    &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
1246 		    DLADM_OPT_ACTIVE);
1247 	}
1248 	ofmt_close(ofmt);
1249 }
1250 
1251 static dladm_status_t
1252 set_flowprop_persist(const char *flow, const char *prop_name, char **prop_val,
1253     uint_t val_cnt, boolean_t reset)
1254 {
1255 	dladm_status_t	status;
1256 	char		*errprop;
1257 
1258 	status = dladm_set_flowprop(handle, flow, prop_name, prop_val, val_cnt,
1259 	    DLADM_OPT_PERSIST, &errprop);
1260 
1261 	if (status != DLADM_STATUS_OK) {
1262 		warn_dlerr(status, "cannot persistently %s flow "
1263 		    "property '%s' on '%s'", reset? "reset": "set",
1264 		    errprop, flow);
1265 	}
1266 	return (status);
1267 }
1268 
1269 static void
1270 set_flowprop(int argc, char **argv, boolean_t reset)
1271 {
1272 	int			i, option;
1273 	char			errmsg[DLADM_STRSIZE];
1274 	const char		*flow = NULL;
1275 	char			propstr[DLADM_STRSIZE];
1276 	dladm_arg_list_t	*proplist = NULL;
1277 	boolean_t		temp = B_FALSE;
1278 	dladm_status_t		status = DLADM_STATUS_OK;
1279 
1280 	opterr = 0;
1281 	bzero(propstr, DLADM_STRSIZE);
1282 
1283 	while ((option = getopt_long(argc, argv, ":p:R:t",
1284 	    prop_longopts, NULL)) != -1) {
1285 		switch (option) {
1286 		case 'p':
1287 			(void) strlcat(propstr, optarg, DLADM_STRSIZE);
1288 			if (strlcat(propstr, ",", DLADM_STRSIZE) >=
1289 			    DLADM_STRSIZE)
1290 				die("property list too long '%s'", propstr);
1291 			break;
1292 		case 't':
1293 			temp = B_TRUE;
1294 			break;
1295 		case 'R':
1296 			status = dladm_set_rootdir(optarg);
1297 			if (status != DLADM_STATUS_OK) {
1298 				die_dlerr(status, "invalid directory "
1299 				    "specified");
1300 			}
1301 			break;
1302 		default:
1303 			die_opterr(optopt, option);
1304 			break;
1305 		}
1306 	}
1307 
1308 	if (optind == (argc - 1)) {
1309 		if (strlen(argv[optind]) >= MAXFLOWNAMELEN)
1310 			die("flow name too long");
1311 		flow = argv[optind];
1312 	} else if (optind != argc) {
1313 		usage();
1314 	}
1315 	if (flow == NULL)
1316 		die("flow name must be specified");
1317 
1318 	if (dladm_parse_flow_props(propstr, &proplist, reset)
1319 	    != DLADM_STATUS_OK)
1320 		die("invalid flow property specified");
1321 
1322 	if (proplist == NULL) {
1323 		char *errprop;
1324 
1325 		if (!reset)
1326 			die("flow property must be specified");
1327 
1328 		status = dladm_set_flowprop(handle, flow, NULL, NULL, 0,
1329 		    DLADM_OPT_ACTIVE, &errprop);
1330 		if (status != DLADM_STATUS_OK) {
1331 			warn_dlerr(status, "cannot reset flow property '%s' "
1332 			    "on '%s'", errprop, flow);
1333 		}
1334 		if (!temp) {
1335 			dladm_status_t	s;
1336 
1337 			s = set_flowprop_persist(flow, NULL, NULL, 0, reset);
1338 			if (s != DLADM_STATUS_OK)
1339 				status = s;
1340 		}
1341 		goto done;
1342 	}
1343 
1344 	for (i = 0; i < proplist->al_count; i++) {
1345 		dladm_arg_info_t	*aip = &proplist->al_info[i];
1346 		char		**val;
1347 		uint_t		count;
1348 		dladm_status_t	s;
1349 
1350 		if (reset) {
1351 			val = NULL;
1352 			count = 0;
1353 		} else {
1354 			val = aip->ai_val;
1355 			count = aip->ai_count;
1356 			if (count == 0) {
1357 				warn("no value specified for '%s'",
1358 				    aip->ai_name);
1359 				status = DLADM_STATUS_BADARG;
1360 				continue;
1361 			}
1362 		}
1363 		s = dladm_set_flowprop(handle, flow, aip->ai_name, val, count,
1364 		    DLADM_OPT_ACTIVE, NULL);
1365 		if (s == DLADM_STATUS_OK) {
1366 			if (!temp) {
1367 				s = set_flowprop_persist(flow,
1368 				    aip->ai_name, val, count, reset);
1369 				if (s != DLADM_STATUS_OK)
1370 					status = s;
1371 			}
1372 			continue;
1373 		}
1374 		status = s;
1375 		switch (s) {
1376 		case DLADM_STATUS_NOTFOUND:
1377 			warn("invalid flow property '%s'", aip->ai_name);
1378 			break;
1379 		case DLADM_STATUS_BADVAL: {
1380 			int		j;
1381 			char		*ptr, *lim;
1382 			char		**propvals = NULL;
1383 			uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
1384 
1385 			ptr = malloc((sizeof (char *) +
1386 			    DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT +
1387 			    MAX_PROP_LINE);
1388 
1389 			if (ptr == NULL)
1390 				die("insufficient memory");
1391 			propvals = (char **)(void *)ptr;
1392 
1393 			for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) {
1394 				propvals[j] = ptr + sizeof (char *) *
1395 				    DLADM_MAX_PROP_VALCNT +
1396 				    j * DLADM_PROP_VAL_MAX;
1397 			}
1398 			s = dladm_get_flowprop(handle, flow,
1399 			    DLADM_PROP_VAL_MODIFIABLE, aip->ai_name, propvals,
1400 			    &valcnt);
1401 
1402 			ptr = errmsg;
1403 			lim = ptr + DLADM_STRSIZE;
1404 			*ptr = '\0';
1405 			for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) {
1406 				ptr += snprintf(ptr, lim - ptr, "%s,",
1407 				    propvals[j]);
1408 				if (ptr >= lim)
1409 					break;
1410 			}
1411 			if (ptr > errmsg) {
1412 				*(ptr - 1) = '\0';
1413 				warn("flow property '%s' must be one of: %s",
1414 				    aip->ai_name, errmsg);
1415 			} else
1416 				warn("%s is an invalid value for "
1417 				    "flow property %s", *val, aip->ai_name);
1418 			free(propvals);
1419 			break;
1420 		}
1421 		default:
1422 			if (reset) {
1423 				warn_dlerr(status, "cannot reset flow property "
1424 				    "'%s' on '%s'", aip->ai_name, flow);
1425 			} else {
1426 				warn_dlerr(status, "cannot set flow property "
1427 				    "'%s' on '%s'", aip->ai_name, flow);
1428 			}
1429 			break;
1430 		}
1431 	}
1432 done:
1433 	dladm_free_props(proplist);
1434 	if (status != DLADM_STATUS_OK) {
1435 		dladm_close(handle);
1436 		exit(EXIT_FAILURE);
1437 	}
1438 }
1439 
1440 static void
1441 do_set_flowprop(int argc, char **argv)
1442 {
1443 	set_flowprop(argc, argv, B_FALSE);
1444 }
1445 
1446 static void
1447 do_reset_flowprop(int argc, char **argv)
1448 {
1449 	set_flowprop(argc, argv, B_TRUE);
1450 }
1451 
1452 static void
1453 warn(const char *format, ...)
1454 {
1455 	va_list alist;
1456 
1457 	format = gettext(format);
1458 	(void) fprintf(stderr, "%s: warning: ", progname);
1459 
1460 	va_start(alist, format);
1461 	(void) vfprintf(stderr, format, alist);
1462 	va_end(alist);
1463 
1464 	(void) putchar('\n');
1465 }
1466 
1467 /* PRINTFLIKE2 */
1468 static void
1469 warn_dlerr(dladm_status_t err, const char *format, ...)
1470 {
1471 	va_list alist;
1472 	char    errmsg[DLADM_STRSIZE];
1473 
1474 	format = gettext(format);
1475 	(void) fprintf(stderr, gettext("%s: warning: "), progname);
1476 
1477 	va_start(alist, format);
1478 	(void) vfprintf(stderr, format, alist);
1479 	va_end(alist);
1480 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
1481 }
1482 
1483 /* PRINTFLIKE1 */
1484 static void
1485 die(const char *format, ...)
1486 {
1487 	va_list alist;
1488 
1489 	format = gettext(format);
1490 	(void) fprintf(stderr, "%s: ", progname);
1491 
1492 	va_start(alist, format);
1493 	(void) vfprintf(stderr, format, alist);
1494 	va_end(alist);
1495 
1496 	(void) putchar('\n');
1497 
1498 	/* close dladm handle if it was opened */
1499 	if (handle != NULL)
1500 		dladm_close(handle);
1501 
1502 	exit(EXIT_FAILURE);
1503 }
1504 
1505 static void
1506 die_optdup(int opt)
1507 {
1508 	die("the option -%c cannot be specified more than once", opt);
1509 }
1510 
1511 static void
1512 die_opterr(int opt, int opterr)
1513 {
1514 	switch (opterr) {
1515 	case ':':
1516 		die("option '-%c' requires a value", opt);
1517 		break;
1518 	case '?':
1519 	default:
1520 		die("unrecognized option '-%c'", opt);
1521 		break;
1522 	}
1523 }
1524 
1525 /* PRINTFLIKE2 */
1526 static void
1527 die_dlerr(dladm_status_t err, const char *format, ...)
1528 {
1529 	va_list alist;
1530 	char	errmsg[DLADM_STRSIZE];
1531 
1532 	format = gettext(format);
1533 	(void) fprintf(stderr, "%s: ", progname);
1534 
1535 	va_start(alist, format);
1536 	(void) vfprintf(stderr, format, alist);
1537 	va_end(alist);
1538 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
1539 
1540 	/* close dladm handle if it was opened */
1541 	if (handle != NULL)
1542 		dladm_close(handle);
1543 
1544 	exit(EXIT_FAILURE);
1545 }
1546 
1547 static void
1548 print_flowprop(const char *flowname, show_flowprop_state_t *statep,
1549     const char *propname, dladm_prop_type_t type,
1550     const char *format, char **pptr)
1551 {
1552 	int		i;
1553 	char		*ptr, *lim;
1554 	char		buf[DLADM_STRSIZE];
1555 	char		*unknown = "--", *notsup = "";
1556 	char		**propvals = statep->fs_propvals;
1557 	uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
1558 	dladm_status_t	status;
1559 
1560 	status = dladm_get_flowprop(handle, flowname, type, propname, propvals,
1561 	    &valcnt);
1562 	if (status != DLADM_STATUS_OK) {
1563 		if (status == DLADM_STATUS_TEMPONLY) {
1564 			if (type == DLADM_PROP_VAL_MODIFIABLE &&
1565 			    statep->fs_persist) {
1566 				valcnt = 1;
1567 				propvals = &unknown;
1568 			} else {
1569 				statep->fs_status = status;
1570 				statep->fs_retstatus = status;
1571 				return;
1572 			}
1573 		} else if (status == DLADM_STATUS_NOTSUP ||
1574 		    statep->fs_persist) {
1575 			valcnt = 1;
1576 			if (type == DLADM_PROP_VAL_CURRENT)
1577 				propvals = &unknown;
1578 			else
1579 				propvals = &notsup;
1580 		} else {
1581 			if ((statep->fs_proplist != NULL) &&
1582 			    statep->fs_status == DLADM_STATUS_OK) {
1583 				warn("invalid flow property '%s'", propname);
1584 			}
1585 			statep->fs_status = status;
1586 			statep->fs_retstatus = status;
1587 			return;
1588 		}
1589 	}
1590 
1591 	statep->fs_status = DLADM_STATUS_OK;
1592 
1593 	ptr = buf;
1594 	lim = buf + DLADM_STRSIZE;
1595 	for (i = 0; i < valcnt; i++) {
1596 		if (propvals[i][0] == '\0' && !statep->fs_parsable)
1597 			ptr += snprintf(ptr, lim - ptr, "--,");
1598 		else
1599 			ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]);
1600 		if (ptr >= lim)
1601 			break;
1602 	}
1603 	if (valcnt > 0)
1604 		buf[strlen(buf) - 1] = '\0';
1605 
1606 	lim = statep->fs_line + MAX_PROP_LINE;
1607 	if (statep->fs_parsable) {
1608 		*pptr += snprintf(*pptr, lim - *pptr,
1609 		    "%s", buf);
1610 	} else {
1611 		*pptr += snprintf(*pptr, lim - *pptr, format, buf);
1612 	}
1613 }
1614 
1615 static boolean_t
1616 print_flowprop_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
1617 {
1618 	flowprop_args_t		*arg = of_arg->ofmt_cbarg;
1619 	char 			*propname = arg->fs_propname;
1620 	show_flowprop_state_t	*statep = arg->fs_state;
1621 	char			*ptr = statep->fs_line;
1622 	char			*lim = ptr + MAX_PROP_LINE;
1623 	char			*flowname = arg->fs_flowname;
1624 
1625 	switch (of_arg->ofmt_id) {
1626 	case FLOWPROP_FLOW:
1627 		(void) snprintf(ptr, lim - ptr, "%s", statep->fs_flow);
1628 		break;
1629 	case FLOWPROP_PROPERTY:
1630 		(void) snprintf(ptr, lim - ptr, "%s", propname);
1631 		break;
1632 	case FLOWPROP_VALUE:
1633 		print_flowprop(flowname, statep, propname,
1634 		    statep->fs_persist ? DLADM_PROP_VAL_PERSISTENT :
1635 		    DLADM_PROP_VAL_CURRENT, "%s", &ptr);
1636 		/*
1637 		 * If we failed to query the flow property, for example, query
1638 		 * the persistent value of a non-persistable flow property,
1639 		 * simply skip the output.
1640 		 */
1641 		if (statep->fs_status != DLADM_STATUS_OK)
1642 			goto skip;
1643 		ptr = statep->fs_line;
1644 		break;
1645 	case FLOWPROP_DEFAULT:
1646 		print_flowprop(flowname, statep, propname,
1647 		    DLADM_PROP_VAL_DEFAULT, "%s", &ptr);
1648 		if (statep->fs_status != DLADM_STATUS_OK)
1649 			goto skip;
1650 		ptr = statep->fs_line;
1651 		break;
1652 	case FLOWPROP_POSSIBLE:
1653 		print_flowprop(flowname, statep, propname,
1654 		    DLADM_PROP_VAL_MODIFIABLE, "%s ", &ptr);
1655 		if (statep->fs_status != DLADM_STATUS_OK)
1656 			goto skip;
1657 		ptr = statep->fs_line;
1658 		break;
1659 	default:
1660 		die("invalid input");
1661 		break;
1662 	}
1663 	(void) strlcpy(buf, ptr, bufsize);
1664 	return (B_TRUE);
1665 skip:
1666 	buf[0] = '\0';
1667 	return ((statep->fs_status == DLADM_STATUS_OK) ?
1668 	    B_TRUE : B_FALSE);
1669 }
1670 
1671 static int
1672 show_one_flowprop(void *arg, const char *propname)
1673 {
1674 	show_flowprop_state_t	*statep = arg;
1675 	flowprop_args_t		fs_arg;
1676 
1677 	bzero(&fs_arg, sizeof (fs_arg));
1678 	fs_arg.fs_state = statep;
1679 	fs_arg.fs_propname = (char *)propname;
1680 	fs_arg.fs_flowname = (char *)statep->fs_flow;
1681 
1682 	ofmt_print(statep->fs_ofmt, (void *)&fs_arg);
1683 
1684 	return (DLADM_WALK_CONTINUE);
1685 }
1686 
1687 /*ARGSUSED*/
1688 /* Walker function called by dladm_walk_flow to display flow properties */
1689 static int
1690 show_flowprop(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
1691 {
1692 	show_flowprop_one_flow(arg, attr->fa_flowname);
1693 	return (DLADM_WALK_CONTINUE);
1694 }
1695 
1696 /*
1697  * Wrapper of dladm_walk_flow(show_walk_fn,...) to make it
1698  * usable to dladm_walk_datalink_id()
1699  */
1700 static int
1701 show_flowprop_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg)
1702 {
1703 	char	name[MAXLINKNAMELEN];
1704 
1705 	if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, name,
1706 	    sizeof (name)) != DLADM_STATUS_OK)
1707 		return (DLADM_WALK_TERMINATE);
1708 
1709 	(void) dladm_walk_flow(show_flowprop, dh, linkid, arg, B_FALSE);
1710 
1711 	return (DLADM_WALK_CONTINUE);
1712 }
1713 
1714 static void
1715 do_show_flowprop(int argc, char **argv)
1716 {
1717 	int			option;
1718 	dladm_arg_list_t	*proplist = NULL;
1719 	show_flowprop_state_t	state;
1720 	char			*fields_str = NULL;
1721 	ofmt_handle_t		ofmt;
1722 	ofmt_status_t		oferr;
1723 	uint_t			ofmtflags = 0;
1724 
1725 	opterr = 0;
1726 	state.fs_propvals = NULL;
1727 	state.fs_line = NULL;
1728 	state.fs_parsable = B_FALSE;
1729 	state.fs_persist = B_FALSE;
1730 	state.fs_header = B_TRUE;
1731 	state.fs_retstatus = DLADM_STATUS_OK;
1732 	state.fs_linkid = DATALINK_INVALID_LINKID;
1733 	state.fs_flow = NULL;
1734 
1735 	while ((option = getopt_long(argc, argv, ":p:cPl:o:",
1736 	    prop_longopts, NULL)) != -1) {
1737 		switch (option) {
1738 		case 'p':
1739 			if (dladm_parse_flow_props(optarg, &proplist, B_TRUE)
1740 			    != DLADM_STATUS_OK)
1741 				die("invalid flow properties specified");
1742 			break;
1743 		case 'c':
1744 			state.fs_parsable = B_TRUE;
1745 			ofmtflags |= OFMT_PARSABLE;
1746 			break;
1747 		case 'P':
1748 			state.fs_persist = B_TRUE;
1749 			break;
1750 		case 'l':
1751 			if (dladm_name2info(handle, optarg, &state.fs_linkid,
1752 			    NULL, NULL, NULL) != DLADM_STATUS_OK)
1753 				die("invalid link '%s'", optarg);
1754 			break;
1755 		case 'o':
1756 			fields_str = optarg;
1757 			break;
1758 		default:
1759 			die_opterr(optopt, option);
1760 			break;
1761 		}
1762 	}
1763 
1764 	if (optind == (argc - 1)) {
1765 		if (strlen(argv[optind]) >= MAXFLOWNAMELEN)
1766 			die("flow name too long");
1767 		state.fs_flow = argv[optind];
1768 	} else if (optind != argc) {
1769 		usage();
1770 	}
1771 	state.fs_proplist = proplist;
1772 	state.fs_status = DLADM_STATUS_OK;
1773 
1774 	oferr = ofmt_open(fields_str, flowprop_fields, ofmtflags, 0, &ofmt);
1775 	flowadm_ofmt_check(oferr, state.fs_parsable, ofmt);
1776 	state.fs_ofmt = ofmt;
1777 
1778 	/* Show properties for one flow */
1779 	if (state.fs_flow != NULL) {
1780 		show_flowprop_one_flow(&state, state.fs_flow);
1781 
1782 	/* Show properties for all flows on one link */
1783 	} else if (state.fs_linkid != DATALINK_INVALID_LINKID) {
1784 		(void) show_flowprop_onelink(handle, state.fs_linkid, &state);
1785 
1786 	/* Show properties for all flows on all links */
1787 	} else {
1788 		(void) dladm_walk_datalink_id(show_flowprop_onelink, handle,
1789 		    &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
1790 		    DLADM_OPT_ACTIVE);
1791 	}
1792 
1793 	dladm_free_props(proplist);
1794 	ofmt_close(ofmt);
1795 }
1796 
1797 static void
1798 show_flowprop_one_flow(void *arg, const char *flow)
1799 {
1800 	int			i;
1801 	char			*buf;
1802 	dladm_status_t		status;
1803 	dladm_arg_list_t	*proplist = NULL;
1804 	show_flowprop_state_t	*statep = arg;
1805 	dladm_flow_attr_t	attr;
1806 	const char		*savep;
1807 
1808 	/*
1809 	 * Do not print flow props for invalid flows.
1810 	 */
1811 	if ((status = dladm_flow_info(handle, flow, &attr)) !=
1812 	    DLADM_STATUS_OK) {
1813 		die("invalid flow: '%s'", flow);
1814 	}
1815 
1816 	savep = statep->fs_flow;
1817 	statep->fs_flow = flow;
1818 
1819 	proplist = statep->fs_proplist;
1820 
1821 	buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX)
1822 	    * DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE);
1823 	if (buf == NULL)
1824 		die("insufficient memory");
1825 
1826 	statep->fs_propvals = (char **)(void *)buf;
1827 	for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
1828 		statep->fs_propvals[i] = buf +
1829 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
1830 		    i * DLADM_PROP_VAL_MAX;
1831 	}
1832 	statep->fs_line = buf +
1833 	    (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT;
1834 
1835 	/* show only specified flow properties */
1836 	if (proplist != NULL) {
1837 		for (i = 0; i < proplist->al_count; i++) {
1838 			if (show_one_flowprop(statep,
1839 			    proplist->al_info[i].ai_name) != DLADM_STATUS_OK)
1840 				break;
1841 		}
1842 
1843 	/* show all flow properties */
1844 	} else {
1845 		status = dladm_walk_flowprop(show_one_flowprop, flow, statep);
1846 		if (status != DLADM_STATUS_OK)
1847 			die_dlerr(status, "show-flowprop");
1848 	}
1849 	free(buf);
1850 	statep->fs_flow = savep;
1851 }
1852 
1853 /*
1854  * default output callback function that, when invoked from dladm_print_output,
1855  * prints string which is offset by of_arg->ofmt_id within buf.
1856  */
1857 static boolean_t
1858 print_default_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
1859 {
1860 	char *value;
1861 
1862 	value = (char *)of_arg->ofmt_cbarg + of_arg->ofmt_id;
1863 	(void) strlcpy(buf, value, bufsize);
1864 	return (B_TRUE);
1865 }
1866 
1867 static void
1868 flowadm_ofmt_check(ofmt_status_t oferr, boolean_t parsable,
1869     ofmt_handle_t ofmt)
1870 {
1871 	char buf[OFMT_BUFSIZE];
1872 
1873 	if (oferr == OFMT_SUCCESS)
1874 		return;
1875 	(void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
1876 	/*
1877 	 * All errors are considered fatal in parsable mode.
1878 	 * NOMEM errors are always fatal, regardless of mode.
1879 	 * For other errors, we print diagnostics in human-readable
1880 	 * mode and processs what we can.
1881 	 */
1882 	if (parsable || oferr == OFMT_ENOFIELDS) {
1883 		ofmt_close(ofmt);
1884 		die(buf);
1885 	} else {
1886 		warn(buf);
1887 	}
1888 }
1889