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