xref: /illumos-gate/usr/src/cmd/flowadm/flowadm.c (revision aec67356a48549d11b3a8a0b63410d67e5e90090)
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 2010 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 <strings.h>
35 #include <getopt.h>
36 #include <unistd.h>
37 #include <priv.h>
38 #include <netdb.h>
39 #include <libintl.h>
40 #include <libdlflow.h>
41 #include <libdllink.h>
42 #include <libdlstat.h>
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47 #include <sys/ethernet.h>
48 #include <inet/ip.h>
49 #include <inet/ip6.h>
50 #include <stddef.h>
51 #include <ofmt.h>
52 
53 typedef struct show_flow_state {
54 	dladm_status_t		fs_status;
55 	ofmt_handle_t		fs_ofmt;
56 	const char		*fs_flow;
57 	boolean_t		fs_parsable;
58 	boolean_t		fs_persist;
59 } show_flow_state_t;
60 
61 typedef void cmdfunc_t(int, char **);
62 
63 static cmdfunc_t do_add_flow, do_remove_flow, do_init_flow, do_show_flow;
64 static cmdfunc_t do_show_flowprop, do_set_flowprop, do_reset_flowprop;
65 
66 static int	show_flow(dladm_handle_t, dladm_flow_attr_t *, void *);
67 static int	show_flows_onelink(dladm_handle_t, datalink_id_t, void *);
68 
69 static int	remove_flow(dladm_handle_t, dladm_flow_attr_t *, void *);
70 
71 static int	show_flowprop(dladm_handle_t, dladm_flow_attr_t *, void *);
72 static void	show_flowprop_one_flow(void *, const char *);
73 static int	show_flowprop_onelink(dladm_handle_t, datalink_id_t, void *);
74 
75 static void	die(const char *, ...);
76 static void	die_optdup(int);
77 static void	die_opterr(int, int);
78 static void	die_dlerr(dladm_status_t, const char *, ...);
79 static void	warn(const char *, ...);
80 static void	warn_dlerr(dladm_status_t, const char *, ...);
81 
82 /* callback functions for printing output */
83 static ofmt_cb_t print_flowprop_cb, print_default_cb;
84 static void flowadm_ofmt_check(ofmt_status_t, boolean_t, ofmt_handle_t);
85 
86 typedef struct	cmd {
87 	char	*c_name;
88 	void	(*c_fn)(int, char **);
89 } cmd_t;
90 
91 static cmd_t	cmds[] = {
92 	{ "add-flow", do_add_flow },
93 	{ "remove-flow", do_remove_flow },
94 	{ "show-flowprop", do_show_flowprop },
95 	{ "set-flowprop", do_set_flowprop },
96 	{ "reset-flowprop", do_reset_flowprop },
97 	{ "show-flow", do_show_flow },
98 	{ "init-flow", do_init_flow },
99 };
100 
101 static const struct option longopts[] = {
102 	{"link",		required_argument,	0, 'l'},
103 	{"parsable",		no_argument,		0, 'p'},
104 	{"parseable",		no_argument,		0, 'p'},
105 	{"temporary",		no_argument,		0, 't'},
106 	{"root-dir",		required_argument,	0, 'R'},
107 	{ 0, 0, 0, 0 }
108 };
109 
110 static const struct option prop_longopts[] = {
111 	{"link",		required_argument,	0, 'l'},
112 	{"temporary",		no_argument,		0, 't'},
113 	{"root-dir",		required_argument,	0, 'R'},
114 	{"prop",		required_argument,	0, 'p'},
115 	{"attr",		required_argument,	0, 'a'},
116 	{ 0, 0, 0, 0 }
117 };
118 
119 /*
120  * structures for 'flowadm remove-flow'
121  */
122 typedef struct remove_flow_state {
123 	boolean_t	fs_tempop;
124 	const char	*fs_altroot;
125 	dladm_status_t	fs_status;
126 } remove_flow_state_t;
127 
128 #define	PROTO_MAXSTR_LEN	7
129 #define	PORT_MAXSTR_LEN		6
130 #define	DSFIELD_MAXSTR_LEN	10
131 #define	NULL_OFMT		{NULL, 0, 0, NULL}
132 
133 typedef struct flow_fields_buf_s
134 {
135 	char flow_name[MAXFLOWNAMELEN];
136 	char flow_link[MAXLINKNAMELEN];
137 	char flow_ipaddr[INET6_ADDRSTRLEN+4];
138 	char flow_proto[PROTO_MAXSTR_LEN];
139 	char flow_lport[PORT_MAXSTR_LEN];
140 	char flow_rport[PORT_MAXSTR_LEN];
141 	char flow_dsfield[DSFIELD_MAXSTR_LEN];
142 } flow_fields_buf_t;
143 
144 static ofmt_field_t flow_fields[] = {
145 /* name,	field width,	index */
146 {  "FLOW",	12,
147 	offsetof(flow_fields_buf_t, flow_name), print_default_cb},
148 {  "LINK",	12,
149 	offsetof(flow_fields_buf_t, flow_link), print_default_cb},
150 {  "IPADDR",	25,
151 	offsetof(flow_fields_buf_t, flow_ipaddr), print_default_cb},
152 {  "PROTO",	7,
153 	offsetof(flow_fields_buf_t, flow_proto), print_default_cb},
154 {  "LPORT",	8,
155 	offsetof(flow_fields_buf_t, flow_lport), print_default_cb},
156 {  "RPORT",	8,
157 	offsetof(flow_fields_buf_t, flow_rport), print_default_cb},
158 {  "DSFLD",	10,
159 	offsetof(flow_fields_buf_t, flow_dsfield), print_default_cb},
160 NULL_OFMT}
161 ;
162 
163 /*
164  * structures for 'flowadm show-flowprop'
165  */
166 typedef enum {
167 	FLOWPROP_FLOW,
168 	FLOWPROP_PROPERTY,
169 	FLOWPROP_VALUE,
170 	FLOWPROP_DEFAULT,
171 	FLOWPROP_POSSIBLE
172 } flowprop_field_index_t;
173 
174 static ofmt_field_t flowprop_fields[] = {
175 /* name,	fieldwidth,	index, 		callback */
176 { "FLOW",	13,	FLOWPROP_FLOW,		print_flowprop_cb},
177 { "PROPERTY",	16,	FLOWPROP_PROPERTY,	print_flowprop_cb},
178 { "VALUE",	15,	FLOWPROP_VALUE,		print_flowprop_cb},
179 { "DEFAULT",	15,	FLOWPROP_DEFAULT,	print_flowprop_cb},
180 { "POSSIBLE",	21,	FLOWPROP_POSSIBLE,	print_flowprop_cb},
181 NULL_OFMT}
182 ;
183 
184 #define	MAX_PROP_LINE		512
185 
186 typedef struct show_flowprop_state {
187 	const char		*fs_flow;
188 	datalink_id_t		fs_linkid;
189 	char			*fs_line;
190 	char			**fs_propvals;
191 	dladm_arg_list_t	*fs_proplist;
192 	boolean_t		fs_parsable;
193 	boolean_t		fs_persist;
194 	boolean_t		fs_header;
195 	dladm_status_t		fs_status;
196 	dladm_status_t		fs_retstatus;
197 	ofmt_handle_t		fs_ofmt;
198 } show_flowprop_state_t;
199 
200 typedef struct set_flowprop_state {
201 	const char	*fs_name;
202 	boolean_t	fs_reset;
203 	boolean_t	fs_temp;
204 	dladm_status_t	fs_status;
205 } set_flowprop_state_t;
206 
207 typedef struct flowprop_args_s {
208 	show_flowprop_state_t	*fs_state;
209 	char			*fs_propname;
210 	char			*fs_flowname;
211 } flowprop_args_t;
212 
213 static char *progname;
214 
215 boolean_t		t_arg = B_FALSE; /* changes are persistent */
216 char			*altroot = NULL;
217 
218 /*
219  * Handle to libdladm.  Opened in main() before the sub-command
220  * specific function is called.
221  */
222 static dladm_handle_t handle = NULL;
223 
224 static const char *attr_table[] =
225 	{"local_ip", "remote_ip", "transport", "local_port", "remote_port",
226 	    "dsfield"};
227 
228 #define	NATTR	(sizeof (attr_table)/sizeof (char *))
229 
230 static void
231 usage(void)
232 {
233 	(void) fprintf(stderr, gettext("usage: flowadm <subcommand>"
234 	    " <args>...\n"
235 	    "    add-flow       [-t] -l <link> -a <attr>=<value>[,...]\n"
236 	    "\t\t   [-p <prop>=<value>,...] <flow>\n"
237 	    "    remove-flow    [-t] {-l <link> | <flow>}\n"
238 	    "    show-flow      [-p] [-l <link>] "
239 	    "[<flow>]\n\n"
240 	    "    set-flowprop   [-t] -p <prop>=<value>[,...] <flow>\n"
241 	    "    reset-flowprop [-t] [-p <prop>,...] <flow>\n"
242 	    "    show-flowprop  [-cP] [-l <link>] [-p <prop>,...] "
243 	    "[<flow>]\n"));
244 
245 	/* close dladm handle if it was opened */
246 	if (handle != NULL)
247 		dladm_close(handle);
248 
249 	exit(1);
250 }
251 
252 int
253 main(int argc, char *argv[])
254 {
255 	int	i, arglen, cmdlen;
256 	cmd_t	*cmdp;
257 	dladm_status_t status;
258 
259 	(void) setlocale(LC_ALL, "");
260 #if !defined(TEXT_DOMAIN)
261 #define	TEXT_DOMAIN "SYS_TEST"
262 #endif
263 	(void) textdomain(TEXT_DOMAIN);
264 
265 	progname = argv[0];
266 
267 	if (argc < 2)
268 		usage();
269 
270 	for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
271 		cmdp = &cmds[i];
272 		arglen = strlen(argv[1]);
273 		cmdlen = strlen(cmdp->c_name);
274 		if ((arglen == cmdlen) && (strncmp(argv[1], cmdp->c_name,
275 		    cmdlen) == 0)) {
276 			/* Open the libdladm handle */
277 			if ((status = dladm_open(&handle)) != DLADM_STATUS_OK) {
278 				die_dlerr(status,
279 				    "could not open /dev/dld");
280 			}
281 
282 			cmdp->c_fn(argc - 1, &argv[1]);
283 
284 			dladm_close(handle);
285 			exit(EXIT_SUCCESS);
286 		}
287 	}
288 
289 	(void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"),
290 	    progname, argv[1]);
291 	usage();
292 
293 	return (0);
294 }
295 
296 static const char *
297 match_attr(char *attr)
298 {
299 	int i;
300 
301 	for (i = 0; i < NATTR; i++) {
302 		if (strlen(attr) == strlen(attr_table[i]) &&
303 		    strncmp(attr, attr_table[i], strlen(attr_table[i])) == 0) {
304 			return (attr);
305 		}
306 	}
307 	return (NULL);
308 }
309 
310 /* ARGSUSED */
311 static void
312 do_init_flow(int argc, char *argv[])
313 {
314 	dladm_status_t status;
315 
316 	status = dladm_flow_init(handle);
317 	if (status != DLADM_STATUS_OK)
318 		die_dlerr(status, "flows initialization failed");
319 }
320 
321 static void
322 do_add_flow(int argc, char *argv[])
323 {
324 	char			devname[MAXLINKNAMELEN];
325 	char			*name = NULL;
326 	uint_t			index;
327 	datalink_id_t		linkid;
328 
329 	char			option;
330 	boolean_t		l_arg = B_FALSE;
331 	char			propstr[DLADM_STRSIZE];
332 	char			attrstr[DLADM_STRSIZE];
333 	dladm_arg_list_t	*proplist = NULL;
334 	dladm_arg_list_t	*attrlist = NULL;
335 	dladm_status_t		status;
336 
337 	bzero(propstr, DLADM_STRSIZE);
338 	bzero(attrstr, DLADM_STRSIZE);
339 
340 	while ((option = getopt_long(argc, argv, "tR:l:a:p:",
341 	    prop_longopts, NULL)) != -1) {
342 		switch (option) {
343 		case 't':
344 			t_arg = B_TRUE;
345 			break;
346 		case 'R':
347 			altroot = optarg;
348 			break;
349 		case 'l':
350 			if (strlcpy(devname, optarg,
351 			    MAXLINKNAMELEN) >= MAXLINKNAMELEN) {
352 				die("link name too long");
353 			}
354 			if (dladm_name2info(handle, devname, &linkid, NULL,
355 			    NULL, NULL) != DLADM_STATUS_OK)
356 				die("invalid link '%s'", devname);
357 			l_arg = B_TRUE;
358 			break;
359 		case 'a':
360 			(void) strlcat(attrstr, optarg, DLADM_STRSIZE);
361 			if (strlcat(attrstr, ",", DLADM_STRSIZE) >=
362 			    DLADM_STRSIZE)
363 				die("attribute list too long '%s'", attrstr);
364 			break;
365 		case 'p':
366 			(void) strlcat(propstr, optarg, DLADM_STRSIZE);
367 			if (strlcat(propstr, ",", DLADM_STRSIZE) >=
368 			    DLADM_STRSIZE)
369 				die("property list too long '%s'", propstr);
370 			break;
371 		default:
372 			die_opterr(optopt, option);
373 		}
374 	}
375 	if (!l_arg) {
376 		die("link is required");
377 	}
378 
379 	opterr = 0;
380 	index = optind;
381 
382 	if ((index != (argc - 1)) || match_attr(argv[index]) != NULL) {
383 		die("flow name is required");
384 	} else {
385 		/* get flow name; required last argument */
386 		if (strlen(argv[index]) >= MAXFLOWNAMELEN)
387 			die("flow name too long");
388 		name = argv[index];
389 	}
390 
391 	if (dladm_parse_flow_attrs(attrstr, &attrlist, B_FALSE)
392 	    != DLADM_STATUS_OK)
393 		die("invalid flow attribute specified");
394 	if (dladm_parse_flow_props(propstr, &proplist, B_FALSE)
395 	    != DLADM_STATUS_OK)
396 		die("invalid flow property specified");
397 
398 	status = dladm_flow_add(handle, linkid, attrlist, proplist, name,
399 	    t_arg, altroot);
400 	if (status != DLADM_STATUS_OK)
401 		die_dlerr(status, "add flow failed");
402 
403 	dladm_free_attrs(attrlist);
404 	dladm_free_props(proplist);
405 }
406 
407 static void
408 do_remove_flow(int argc, char *argv[])
409 {
410 	char			option;
411 	char			*flowname = NULL;
412 	char			linkname[MAXLINKNAMELEN];
413 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
414 	boolean_t		l_arg = B_FALSE;
415 	remove_flow_state_t	state;
416 	dladm_status_t		status;
417 
418 	bzero(&state, sizeof (state));
419 
420 	opterr = 0;
421 	while ((option = getopt_long(argc, argv, ":tR:l:",
422 	    longopts, NULL)) != -1) {
423 		switch (option) {
424 		case 't':
425 			t_arg = B_TRUE;
426 			break;
427 		case 'R':
428 			altroot = optarg;
429 			break;
430 		case 'l':
431 			if (strlcpy(linkname, optarg,
432 			    MAXLINKNAMELEN) >= MAXLINKNAMELEN) {
433 				die("link name too long");
434 			}
435 			if (dladm_name2info(handle, linkname, &linkid, NULL,
436 			    NULL, NULL) != DLADM_STATUS_OK) {
437 				die("invalid link '%s'", linkname);
438 			}
439 			l_arg = B_TRUE;
440 			break;
441 		default:
442 			die_opterr(optopt, option);
443 			break;
444 		}
445 	}
446 
447 	/* when link not specified get flow name */
448 	if (!l_arg) {
449 		if (optind != (argc-1)) {
450 			usage();
451 		} else {
452 			if (strlen(argv[optind]) >= MAXFLOWNAMELEN)
453 				die("flow name too long");
454 			flowname = argv[optind];
455 		}
456 		status = dladm_flow_remove(handle, flowname, t_arg, altroot);
457 	} else {
458 		/* if link is specified then flow name should not be there */
459 		if (optind == argc-1)
460 			usage();
461 		/* walk the link to find flows and remove them */
462 		state.fs_tempop = t_arg;
463 		state.fs_altroot = altroot;
464 		state.fs_status = DLADM_STATUS_OK;
465 		status = dladm_walk_flow(remove_flow, handle, linkid, &state,
466 		    B_FALSE);
467 		/*
468 		 * check if dladm_walk_flow terminated early and see if the
469 		 * walker function as any status for us
470 		 */
471 		if (status == DLADM_STATUS_OK)
472 			status = state.fs_status;
473 	}
474 
475 	if (status != DLADM_STATUS_OK)
476 		die_dlerr(status, "remove flow failed");
477 }
478 
479 /*
480  * Walker function for removing a flow through dladm_walk_flow();
481  */
482 /*ARGSUSED*/
483 static int
484 remove_flow(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
485 {
486 	remove_flow_state_t	*state = (remove_flow_state_t *)arg;
487 
488 	state->fs_status = dladm_flow_remove(handle, attr->fa_flowname,
489 	    state->fs_tempop, state->fs_altroot);
490 
491 	if (state->fs_status == DLADM_STATUS_OK)
492 		return (DLADM_WALK_CONTINUE);
493 	else
494 		return (DLADM_WALK_TERMINATE);
495 }
496 
497 /*ARGSUSED*/
498 static dladm_status_t
499 print_flow(show_flow_state_t *state, dladm_flow_attr_t *attr,
500     flow_fields_buf_t *fbuf)
501 {
502 	char		link[MAXLINKNAMELEN];
503 	dladm_status_t	status;
504 
505 	if ((status = dladm_datalink_id2info(handle, attr->fa_linkid, NULL,
506 	    NULL, NULL, link, sizeof (link))) != DLADM_STATUS_OK) {
507 		return (status);
508 	}
509 
510 	(void) snprintf(fbuf->flow_name, sizeof (fbuf->flow_name),
511 	    "%s", attr->fa_flowname);
512 	(void) snprintf(fbuf->flow_link, sizeof (fbuf->flow_link),
513 	    "%s", link);
514 
515 	(void) dladm_flow_attr_ip2str(attr, fbuf->flow_ipaddr,
516 	    sizeof (fbuf->flow_ipaddr));
517 	(void) dladm_flow_attr_proto2str(attr, fbuf->flow_proto,
518 	    sizeof (fbuf->flow_proto));
519 	if ((attr->fa_flow_desc.fd_mask & FLOW_ULP_PORT_LOCAL) != 0) {
520 		(void) dladm_flow_attr_port2str(attr, fbuf->flow_lport,
521 		    sizeof (fbuf->flow_lport));
522 	}
523 	if ((attr->fa_flow_desc.fd_mask & FLOW_ULP_PORT_REMOTE) != 0) {
524 		(void) dladm_flow_attr_port2str(attr, fbuf->flow_rport,
525 		    sizeof (fbuf->flow_rport));
526 	}
527 	(void) dladm_flow_attr_dsfield2str(attr, fbuf->flow_dsfield,
528 	    sizeof (fbuf->flow_dsfield));
529 
530 	return (DLADM_STATUS_OK);
531 }
532 
533 /*
534  * Walker function for showing flow attributes through dladm_walk_flow().
535  */
536 /*ARGSUSED*/
537 static int
538 show_flow(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
539 {
540 	show_flow_state_t	*statep = arg;
541 	dladm_status_t		status;
542 	flow_fields_buf_t	fbuf;
543 
544 	/*
545 	 * first get all the flow attributes into fbuf;
546 	 */
547 	bzero(&fbuf, sizeof (fbuf));
548 	status = print_flow(statep, attr, &fbuf);
549 
550 	if (status != DLADM_STATUS_OK)
551 		goto done;
552 
553 	ofmt_print(statep->fs_ofmt, (void *)&fbuf);
554 
555 done:
556 	statep->fs_status = status;
557 	return (DLADM_WALK_CONTINUE);
558 }
559 
560 static void
561 show_one_flow(void *arg, const char *name)
562 {
563 	dladm_flow_attr_t	attr;
564 
565 	if (dladm_flow_info(handle, name, &attr) != DLADM_STATUS_OK)
566 		die("invalid flow: '%s'", name);
567 	else
568 		(void) show_flow(handle, &attr, arg);
569 }
570 
571 /*
572  * Wrapper of dladm_walk_flow(show_flow,...) to make it usable to
573  * dladm_walk_datalink_id(). Used for showing flow attributes for
574  * all flows on all links.
575  */
576 static int
577 show_flows_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg)
578 {
579 	show_flow_state_t *state = arg;
580 
581 	(void) dladm_walk_flow(show_flow, dh, linkid, arg, state->fs_persist);
582 
583 	return (DLADM_WALK_CONTINUE);
584 }
585 
586 static void
587 do_show_flow(int argc, char *argv[])
588 {
589 	char			flowname[MAXFLOWNAMELEN];
590 	char			linkname[MAXLINKNAMELEN];
591 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
592 	int			option;
593 	boolean_t		l_arg = B_FALSE;
594 	boolean_t		o_arg = B_FALSE;
595 	show_flow_state_t	state;
596 	char			*fields_str = NULL;
597 	ofmt_handle_t		ofmt;
598 	ofmt_status_t		oferr;
599 	uint_t			ofmtflags = 0;
600 
601 	bzero(&state, sizeof (state));
602 
603 	opterr = 0;
604 	while ((option = getopt_long(argc, argv, ":pPl:o:",
605 	    longopts, NULL)) != -1) {
606 		switch (option) {
607 		case 'p':
608 			state.fs_parsable = B_TRUE;
609 			ofmtflags |= OFMT_PARSABLE;
610 			break;
611 		case 'P':
612 			state.fs_persist = B_TRUE;
613 			break;
614 		case 'o':
615 			if (o_arg)
616 				die_optdup(option);
617 
618 			o_arg = B_TRUE;
619 			fields_str = optarg;
620 			break;
621 		case 'l':
622 			if (strlcpy(linkname, optarg, MAXLINKNAMELEN)
623 			    >= MAXLINKNAMELEN)
624 				die("link name too long\n");
625 			if (dladm_name2info(handle, linkname, &linkid, NULL,
626 			    NULL, NULL) != DLADM_STATUS_OK)
627 				die("invalid link '%s'", linkname);
628 			l_arg = B_TRUE;
629 			break;
630 		default:
631 			die_opterr(optopt, option);
632 			break;
633 		}
634 	}
635 
636 	/* get flow name (optional last argument */
637 	if (optind == (argc-1)) {
638 		if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN)
639 		    >= MAXFLOWNAMELEN)
640 			die("flow name too long");
641 		state.fs_flow = flowname;
642 	}
643 
644 	oferr = ofmt_open(fields_str, flow_fields, ofmtflags, 0, &ofmt);
645 	flowadm_ofmt_check(oferr, state.fs_parsable, ofmt);
646 	state.fs_ofmt = ofmt;
647 
648 	/* Show attributes of one flow */
649 	if (state.fs_flow != NULL) {
650 		show_one_flow(&state, state.fs_flow);
651 
652 	/* Show attributes of flows on one link */
653 	} else if (l_arg) {
654 		(void) show_flows_onelink(handle, linkid, &state);
655 
656 	/* Show attributes of all flows on all links */
657 	} else {
658 		(void) dladm_walk_datalink_id(show_flows_onelink, handle,
659 		    &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
660 		    DLADM_OPT_ACTIVE);
661 	}
662 	ofmt_close(ofmt);
663 }
664 
665 static dladm_status_t
666 set_flowprop_persist(const char *flow, const char *prop_name, char **prop_val,
667     uint_t val_cnt, boolean_t reset)
668 {
669 	dladm_status_t	status;
670 	char		*errprop;
671 
672 	status = dladm_set_flowprop(handle, flow, prop_name, prop_val, val_cnt,
673 	    DLADM_OPT_PERSIST, &errprop);
674 
675 	if (status != DLADM_STATUS_OK) {
676 		warn_dlerr(status, "cannot persistently %s flow "
677 		    "property '%s' on '%s'", reset? "reset": "set",
678 		    errprop, flow);
679 	}
680 	return (status);
681 }
682 
683 static void
684 set_flowprop(int argc, char **argv, boolean_t reset)
685 {
686 	int			i, option;
687 	char			errmsg[DLADM_STRSIZE];
688 	const char		*flow = NULL;
689 	char			propstr[DLADM_STRSIZE];
690 	dladm_arg_list_t	*proplist = NULL;
691 	boolean_t		temp = B_FALSE;
692 	dladm_status_t		status = DLADM_STATUS_OK;
693 
694 	opterr = 0;
695 	bzero(propstr, DLADM_STRSIZE);
696 
697 	while ((option = getopt_long(argc, argv, ":p:R:t",
698 	    prop_longopts, NULL)) != -1) {
699 		switch (option) {
700 		case 'p':
701 			(void) strlcat(propstr, optarg, DLADM_STRSIZE);
702 			if (strlcat(propstr, ",", DLADM_STRSIZE) >=
703 			    DLADM_STRSIZE)
704 				die("property list too long '%s'", propstr);
705 			break;
706 		case 't':
707 			temp = B_TRUE;
708 			break;
709 		case 'R':
710 			status = dladm_set_rootdir(optarg);
711 			if (status != DLADM_STATUS_OK) {
712 				die_dlerr(status, "invalid directory "
713 				    "specified");
714 			}
715 			break;
716 		default:
717 			die_opterr(optopt, option);
718 			break;
719 		}
720 	}
721 
722 	if (optind == (argc - 1)) {
723 		if (strlen(argv[optind]) >= MAXFLOWNAMELEN)
724 			die("flow name too long");
725 		flow = argv[optind];
726 	} else if (optind != argc) {
727 		usage();
728 	}
729 	if (flow == NULL)
730 		die("flow name must be specified");
731 
732 	if (dladm_parse_flow_props(propstr, &proplist, reset)
733 	    != DLADM_STATUS_OK)
734 		die("invalid flow property specified");
735 
736 	if (proplist == NULL) {
737 		char *errprop;
738 
739 		if (!reset)
740 			die("flow property must be specified");
741 
742 		status = dladm_set_flowprop(handle, flow, NULL, NULL, 0,
743 		    DLADM_OPT_ACTIVE, &errprop);
744 		if (status != DLADM_STATUS_OK) {
745 			warn_dlerr(status, "cannot reset flow property '%s' "
746 			    "on '%s'", errprop, flow);
747 		}
748 		if (!temp) {
749 			dladm_status_t	s;
750 
751 			s = set_flowprop_persist(flow, NULL, NULL, 0, reset);
752 			if (s != DLADM_STATUS_OK)
753 				status = s;
754 		}
755 		goto done;
756 	}
757 
758 	for (i = 0; i < proplist->al_count; i++) {
759 		dladm_arg_info_t	*aip = &proplist->al_info[i];
760 		char		**val;
761 		uint_t		count;
762 		dladm_status_t	s;
763 
764 		if (reset) {
765 			val = NULL;
766 			count = 0;
767 		} else {
768 			val = aip->ai_val;
769 			count = aip->ai_count;
770 			if (count == 0) {
771 				warn("no value specified for '%s'",
772 				    aip->ai_name);
773 				status = DLADM_STATUS_BADARG;
774 				continue;
775 			}
776 		}
777 		s = dladm_set_flowprop(handle, flow, aip->ai_name, val, count,
778 		    DLADM_OPT_ACTIVE, NULL);
779 		if (s == DLADM_STATUS_OK) {
780 			if (!temp) {
781 				s = set_flowprop_persist(flow,
782 				    aip->ai_name, val, count, reset);
783 				if (s != DLADM_STATUS_OK)
784 					status = s;
785 			}
786 			continue;
787 		}
788 		status = s;
789 		switch (s) {
790 		case DLADM_STATUS_NOTFOUND:
791 			warn("invalid flow property '%s'", aip->ai_name);
792 			break;
793 		case DLADM_STATUS_BADVAL: {
794 			int		j;
795 			char		*ptr, *lim;
796 			char		**propvals = NULL;
797 			uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
798 
799 			ptr = malloc((sizeof (char *) +
800 			    DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT +
801 			    MAX_PROP_LINE);
802 
803 			if (ptr == NULL)
804 				die("insufficient memory");
805 			propvals = (char **)(void *)ptr;
806 
807 			for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) {
808 				propvals[j] = ptr + sizeof (char *) *
809 				    DLADM_MAX_PROP_VALCNT +
810 				    j * DLADM_PROP_VAL_MAX;
811 			}
812 			s = dladm_get_flowprop(handle, flow,
813 			    DLADM_PROP_VAL_MODIFIABLE, aip->ai_name, propvals,
814 			    &valcnt);
815 
816 			ptr = errmsg;
817 			lim = ptr + DLADM_STRSIZE;
818 			*ptr = '\0';
819 			for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) {
820 				ptr += snprintf(ptr, lim - ptr, "%s,",
821 				    propvals[j]);
822 				if (ptr >= lim)
823 					break;
824 			}
825 			if (ptr > errmsg) {
826 				*(ptr - 1) = '\0';
827 				warn("flow property '%s' must be one of: %s",
828 				    aip->ai_name, errmsg);
829 			} else
830 				warn("%s is an invalid value for "
831 				    "flow property %s", *val, aip->ai_name);
832 			free(propvals);
833 			break;
834 		}
835 		default:
836 			if (reset) {
837 				warn_dlerr(status, "cannot reset flow property "
838 				    "'%s' on '%s'", aip->ai_name, flow);
839 			} else {
840 				warn_dlerr(status, "cannot set flow property "
841 				    "'%s' on '%s'", aip->ai_name, flow);
842 			}
843 			break;
844 		}
845 	}
846 done:
847 	dladm_free_props(proplist);
848 	if (status != DLADM_STATUS_OK) {
849 		dladm_close(handle);
850 		exit(EXIT_FAILURE);
851 	}
852 }
853 
854 static void
855 do_set_flowprop(int argc, char **argv)
856 {
857 	set_flowprop(argc, argv, B_FALSE);
858 }
859 
860 static void
861 do_reset_flowprop(int argc, char **argv)
862 {
863 	set_flowprop(argc, argv, B_TRUE);
864 }
865 
866 static void
867 warn(const char *format, ...)
868 {
869 	va_list alist;
870 
871 	format = gettext(format);
872 	(void) fprintf(stderr, "%s: warning: ", progname);
873 
874 	va_start(alist, format);
875 	(void) vfprintf(stderr, format, alist);
876 	va_end(alist);
877 
878 	(void) putc('\n', stderr);
879 }
880 
881 /* PRINTFLIKE2 */
882 static void
883 warn_dlerr(dladm_status_t err, const char *format, ...)
884 {
885 	va_list alist;
886 	char    errmsg[DLADM_STRSIZE];
887 
888 	format = gettext(format);
889 	(void) fprintf(stderr, gettext("%s: warning: "), progname);
890 
891 	va_start(alist, format);
892 	(void) vfprintf(stderr, format, alist);
893 	va_end(alist);
894 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
895 }
896 
897 /* PRINTFLIKE1 */
898 static void
899 die(const char *format, ...)
900 {
901 	va_list alist;
902 
903 	format = gettext(format);
904 	(void) fprintf(stderr, "%s: ", progname);
905 
906 	va_start(alist, format);
907 	(void) vfprintf(stderr, format, alist);
908 	va_end(alist);
909 
910 	(void) putc('\n', stderr);
911 
912 	/* close dladm handle if it was opened */
913 	if (handle != NULL)
914 		dladm_close(handle);
915 
916 	exit(EXIT_FAILURE);
917 }
918 
919 static void
920 die_optdup(int opt)
921 {
922 	die("the option -%c cannot be specified more than once", opt);
923 }
924 
925 static void
926 die_opterr(int opt, int opterr)
927 {
928 	switch (opterr) {
929 	case ':':
930 		die("option '-%c' requires a value", opt);
931 		break;
932 	case '?':
933 	default:
934 		die("unrecognized option '-%c'", opt);
935 		break;
936 	}
937 }
938 
939 /* PRINTFLIKE2 */
940 static void
941 die_dlerr(dladm_status_t err, const char *format, ...)
942 {
943 	va_list alist;
944 	char	errmsg[DLADM_STRSIZE];
945 
946 	format = gettext(format);
947 	(void) fprintf(stderr, "%s: ", progname);
948 
949 	va_start(alist, format);
950 	(void) vfprintf(stderr, format, alist);
951 	va_end(alist);
952 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
953 
954 	/* close dladm handle if it was opened */
955 	if (handle != NULL)
956 		dladm_close(handle);
957 
958 	exit(EXIT_FAILURE);
959 }
960 
961 static void
962 print_flowprop(const char *flowname, show_flowprop_state_t *statep,
963     const char *propname, dladm_prop_type_t type,
964     const char *format, char **pptr)
965 {
966 	int		i;
967 	char		*ptr, *lim;
968 	char		buf[DLADM_STRSIZE];
969 	char		*unknown = "--", *notsup = "";
970 	char		**propvals = statep->fs_propvals;
971 	uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
972 	dladm_status_t	status;
973 
974 	status = dladm_get_flowprop(handle, flowname, type, propname, propvals,
975 	    &valcnt);
976 	if (status != DLADM_STATUS_OK) {
977 		if (status == DLADM_STATUS_TEMPONLY) {
978 			if (type == DLADM_PROP_VAL_MODIFIABLE &&
979 			    statep->fs_persist) {
980 				valcnt = 1;
981 				propvals = &unknown;
982 			} else {
983 				statep->fs_status = status;
984 				statep->fs_retstatus = status;
985 				return;
986 			}
987 		} else if (status == DLADM_STATUS_NOTSUP ||
988 		    statep->fs_persist) {
989 			valcnt = 1;
990 			if (type == DLADM_PROP_VAL_CURRENT)
991 				propvals = &unknown;
992 			else
993 				propvals = &notsup;
994 		} else {
995 			if ((statep->fs_proplist != NULL) &&
996 			    statep->fs_status == DLADM_STATUS_OK) {
997 				warn("invalid flow property '%s'", propname);
998 			}
999 			statep->fs_status = status;
1000 			statep->fs_retstatus = status;
1001 			return;
1002 		}
1003 	}
1004 
1005 	statep->fs_status = DLADM_STATUS_OK;
1006 
1007 	ptr = buf;
1008 	lim = buf + DLADM_STRSIZE;
1009 	for (i = 0; i < valcnt; i++) {
1010 		if (propvals[i][0] == '\0' && !statep->fs_parsable)
1011 			ptr += snprintf(ptr, lim - ptr, "--,");
1012 		else
1013 			ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]);
1014 		if (ptr >= lim)
1015 			break;
1016 	}
1017 	if (valcnt > 0)
1018 		buf[strlen(buf) - 1] = '\0';
1019 
1020 	lim = statep->fs_line + MAX_PROP_LINE;
1021 	if (statep->fs_parsable) {
1022 		*pptr += snprintf(*pptr, lim - *pptr,
1023 		    "%s", buf);
1024 	} else {
1025 		*pptr += snprintf(*pptr, lim - *pptr, format, buf);
1026 	}
1027 }
1028 
1029 static boolean_t
1030 print_flowprop_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
1031 {
1032 	flowprop_args_t		*arg = of_arg->ofmt_cbarg;
1033 	char 			*propname = arg->fs_propname;
1034 	show_flowprop_state_t	*statep = arg->fs_state;
1035 	char			*ptr = statep->fs_line;
1036 	char			*lim = ptr + MAX_PROP_LINE;
1037 	char			*flowname = arg->fs_flowname;
1038 
1039 	switch (of_arg->ofmt_id) {
1040 	case FLOWPROP_FLOW:
1041 		(void) snprintf(ptr, lim - ptr, "%s", statep->fs_flow);
1042 		break;
1043 	case FLOWPROP_PROPERTY:
1044 		(void) snprintf(ptr, lim - ptr, "%s", propname);
1045 		break;
1046 	case FLOWPROP_VALUE:
1047 		print_flowprop(flowname, statep, propname,
1048 		    statep->fs_persist ? DLADM_PROP_VAL_PERSISTENT :
1049 		    DLADM_PROP_VAL_CURRENT, "%s", &ptr);
1050 		/*
1051 		 * If we failed to query the flow property, for example, query
1052 		 * the persistent value of a non-persistable flow property,
1053 		 * simply skip the output.
1054 		 */
1055 		if (statep->fs_status != DLADM_STATUS_OK)
1056 			goto skip;
1057 		ptr = statep->fs_line;
1058 		break;
1059 	case FLOWPROP_DEFAULT:
1060 		print_flowprop(flowname, statep, propname,
1061 		    DLADM_PROP_VAL_DEFAULT, "%s", &ptr);
1062 		if (statep->fs_status != DLADM_STATUS_OK)
1063 			goto skip;
1064 		ptr = statep->fs_line;
1065 		break;
1066 	case FLOWPROP_POSSIBLE:
1067 		print_flowprop(flowname, statep, propname,
1068 		    DLADM_PROP_VAL_MODIFIABLE, "%s ", &ptr);
1069 		if (statep->fs_status != DLADM_STATUS_OK)
1070 			goto skip;
1071 		ptr = statep->fs_line;
1072 		break;
1073 	default:
1074 		die("invalid input");
1075 		break;
1076 	}
1077 	(void) strlcpy(buf, ptr, bufsize);
1078 	return (B_TRUE);
1079 skip:
1080 	buf[0] = '\0';
1081 	return ((statep->fs_status == DLADM_STATUS_OK) ?
1082 	    B_TRUE : B_FALSE);
1083 }
1084 
1085 static int
1086 show_one_flowprop(void *arg, const char *propname)
1087 {
1088 	show_flowprop_state_t	*statep = arg;
1089 	flowprop_args_t		fs_arg;
1090 
1091 	bzero(&fs_arg, sizeof (fs_arg));
1092 	fs_arg.fs_state = statep;
1093 	fs_arg.fs_propname = (char *)propname;
1094 	fs_arg.fs_flowname = (char *)statep->fs_flow;
1095 
1096 	ofmt_print(statep->fs_ofmt, (void *)&fs_arg);
1097 
1098 	return (DLADM_WALK_CONTINUE);
1099 }
1100 
1101 /*ARGSUSED*/
1102 /* Walker function called by dladm_walk_flow to display flow properties */
1103 static int
1104 show_flowprop(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
1105 {
1106 	show_flowprop_one_flow(arg, attr->fa_flowname);
1107 	return (DLADM_WALK_CONTINUE);
1108 }
1109 
1110 /*
1111  * Wrapper of dladm_walk_flow(show_walk_fn,...) to make it
1112  * usable to dladm_walk_datalink_id()
1113  */
1114 static int
1115 show_flowprop_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg)
1116 {
1117 	char	name[MAXLINKNAMELEN];
1118 
1119 	if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, name,
1120 	    sizeof (name)) != DLADM_STATUS_OK)
1121 		return (DLADM_WALK_TERMINATE);
1122 
1123 	(void) dladm_walk_flow(show_flowprop, dh, linkid, arg, B_FALSE);
1124 
1125 	return (DLADM_WALK_CONTINUE);
1126 }
1127 
1128 static void
1129 do_show_flowprop(int argc, char **argv)
1130 {
1131 	int			option;
1132 	dladm_arg_list_t	*proplist = NULL;
1133 	show_flowprop_state_t	state;
1134 	char			*fields_str = NULL;
1135 	ofmt_handle_t		ofmt;
1136 	ofmt_status_t		oferr;
1137 	uint_t			ofmtflags = 0;
1138 
1139 	opterr = 0;
1140 	state.fs_propvals = NULL;
1141 	state.fs_line = NULL;
1142 	state.fs_parsable = B_FALSE;
1143 	state.fs_persist = B_FALSE;
1144 	state.fs_header = B_TRUE;
1145 	state.fs_retstatus = DLADM_STATUS_OK;
1146 	state.fs_linkid = DATALINK_INVALID_LINKID;
1147 	state.fs_flow = NULL;
1148 
1149 	while ((option = getopt_long(argc, argv, ":p:cPl:o:",
1150 	    prop_longopts, NULL)) != -1) {
1151 		switch (option) {
1152 		case 'p':
1153 			if (dladm_parse_flow_props(optarg, &proplist, B_TRUE)
1154 			    != DLADM_STATUS_OK)
1155 				die("invalid flow properties specified");
1156 			break;
1157 		case 'c':
1158 			state.fs_parsable = B_TRUE;
1159 			ofmtflags |= OFMT_PARSABLE;
1160 			break;
1161 		case 'P':
1162 			state.fs_persist = B_TRUE;
1163 			break;
1164 		case 'l':
1165 			if (dladm_name2info(handle, optarg, &state.fs_linkid,
1166 			    NULL, NULL, NULL) != DLADM_STATUS_OK)
1167 				die("invalid link '%s'", optarg);
1168 			break;
1169 		case 'o':
1170 			fields_str = optarg;
1171 			break;
1172 		default:
1173 			die_opterr(optopt, option);
1174 			break;
1175 		}
1176 	}
1177 
1178 	if (optind == (argc - 1)) {
1179 		if (strlen(argv[optind]) >= MAXFLOWNAMELEN)
1180 			die("flow name too long");
1181 		state.fs_flow = argv[optind];
1182 	} else if (optind != argc) {
1183 		usage();
1184 	}
1185 	state.fs_proplist = proplist;
1186 	state.fs_status = DLADM_STATUS_OK;
1187 
1188 	oferr = ofmt_open(fields_str, flowprop_fields, ofmtflags, 0, &ofmt);
1189 	flowadm_ofmt_check(oferr, state.fs_parsable, ofmt);
1190 	state.fs_ofmt = ofmt;
1191 
1192 	/* Show properties for one flow */
1193 	if (state.fs_flow != NULL) {
1194 		show_flowprop_one_flow(&state, state.fs_flow);
1195 
1196 	/* Show properties for all flows on one link */
1197 	} else if (state.fs_linkid != DATALINK_INVALID_LINKID) {
1198 		(void) show_flowprop_onelink(handle, state.fs_linkid, &state);
1199 
1200 	/* Show properties for all flows on all links */
1201 	} else {
1202 		(void) dladm_walk_datalink_id(show_flowprop_onelink, handle,
1203 		    &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
1204 		    DLADM_OPT_ACTIVE);
1205 	}
1206 
1207 	dladm_free_props(proplist);
1208 	ofmt_close(ofmt);
1209 }
1210 
1211 static void
1212 show_flowprop_one_flow(void *arg, const char *flow)
1213 {
1214 	int			i;
1215 	char			*buf;
1216 	dladm_status_t		status;
1217 	dladm_arg_list_t	*proplist = NULL;
1218 	show_flowprop_state_t	*statep = arg;
1219 	dladm_flow_attr_t	attr;
1220 	const char		*savep;
1221 
1222 	/*
1223 	 * Do not print flow props for invalid flows.
1224 	 */
1225 	if ((status = dladm_flow_info(handle, flow, &attr)) !=
1226 	    DLADM_STATUS_OK) {
1227 		die("invalid flow: '%s'", flow);
1228 	}
1229 
1230 	savep = statep->fs_flow;
1231 	statep->fs_flow = flow;
1232 
1233 	proplist = statep->fs_proplist;
1234 
1235 	buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX)
1236 	    * DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE);
1237 	if (buf == NULL)
1238 		die("insufficient memory");
1239 
1240 	statep->fs_propvals = (char **)(void *)buf;
1241 	for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
1242 		statep->fs_propvals[i] = buf +
1243 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
1244 		    i * DLADM_PROP_VAL_MAX;
1245 	}
1246 	statep->fs_line = buf +
1247 	    (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT;
1248 
1249 	/* show only specified flow properties */
1250 	if (proplist != NULL) {
1251 		for (i = 0; i < proplist->al_count; i++) {
1252 			if (show_one_flowprop(statep,
1253 			    proplist->al_info[i].ai_name) != DLADM_STATUS_OK)
1254 				break;
1255 		}
1256 
1257 	/* show all flow properties */
1258 	} else {
1259 		status = dladm_walk_flowprop(show_one_flowprop, flow, statep);
1260 		if (status != DLADM_STATUS_OK)
1261 			die_dlerr(status, "show-flowprop");
1262 	}
1263 	free(buf);
1264 	statep->fs_flow = savep;
1265 }
1266 
1267 /*
1268  * default output callback function that, when invoked from dladm_print_output,
1269  * prints string which is offset by of_arg->ofmt_id within buf.
1270  */
1271 static boolean_t
1272 print_default_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
1273 {
1274 	char *value;
1275 
1276 	value = (char *)of_arg->ofmt_cbarg + of_arg->ofmt_id;
1277 	(void) strlcpy(buf, value, bufsize);
1278 	return (B_TRUE);
1279 }
1280 
1281 static void
1282 flowadm_ofmt_check(ofmt_status_t oferr, boolean_t parsable,
1283     ofmt_handle_t ofmt)
1284 {
1285 	char buf[OFMT_BUFSIZE];
1286 
1287 	if (oferr == OFMT_SUCCESS)
1288 		return;
1289 	(void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
1290 	/*
1291 	 * All errors are considered fatal in parsable mode.
1292 	 * NOMEM errors are always fatal, regardless of mode.
1293 	 * For other errors, we print diagnostics in human-readable
1294 	 * mode and processs what we can.
1295 	 */
1296 	if (parsable || oferr == OFMT_ENOFIELDS) {
1297 		ofmt_close(ofmt);
1298 		die(buf);
1299 	} else {
1300 		warn(buf);
1301 	}
1302 }
1303