xref: /titanic_41/usr/src/lib/libdladm/common/linkprop.c (revision f4da9be01648d6afa604495d9b9956dddeb92a8c)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdlib.h>
29 #include <strings.h>
30 #include <errno.h>
31 #include <ctype.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/dld.h>
35 #include <sys/zone.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <libdevinfo.h>
39 #include <zone.h>
40 #include <libdllink.h>
41 #include <libdladm_impl.h>
42 #include <libdlwlan.h>
43 #include <dlfcn.h>
44 #include <link.h>
45 
46 static dladm_status_t	i_dladm_set_prop_db(const char *, const char *,
47 			    char **, uint_t);
48 static dladm_status_t	i_dladm_get_prop_db(const char *, const char *,
49 			    char **, uint_t *);
50 static dladm_status_t	i_dladm_get_prop_temp(const char *, dladm_prop_type_t,
51 			    const char *, char **, uint_t *);
52 static dladm_status_t	i_dladm_set_prop_temp(const char *, const char *,
53 			    char **, uint_t, uint_t, char **);
54 static boolean_t	i_dladm_is_prop_temponly(const char *prop_name,
55 			    char **);
56 
57 typedef struct val_desc {
58 	char	*vd_name;
59 	void	*vd_val;
60 } val_desc_t;
61 
62 struct prop_desc;
63 
64 typedef dladm_status_t	pd_getf_t(const char *, char **, uint_t *);
65 typedef dladm_status_t	pd_setf_t(const char *, val_desc_t *, uint_t);
66 typedef dladm_status_t	pd_checkf_t(struct prop_desc *, char **,
67 			    uint_t, val_desc_t **);
68 
69 static pd_getf_t	do_get_zone;
70 static pd_setf_t	do_set_zone;
71 static pd_checkf_t	do_check_zone;
72 
73 typedef struct prop_desc {
74 	char		*pd_name;
75 	val_desc_t	pd_defval;
76 	val_desc_t	*pd_modval;
77 	uint_t		pd_nmodval;
78 	boolean_t	pd_temponly;
79 	pd_setf_t	*pd_set;
80 	pd_getf_t	*pd_getmod;
81 	pd_getf_t	*pd_get;
82 	pd_checkf_t	*pd_check;
83 } prop_desc_t;
84 
85 static prop_desc_t	prop_table[] = {
86 	{ "zone",	{ "", NULL }, NULL, 0, B_TRUE,
87 	    do_set_zone, NULL,
88 	    do_get_zone, do_check_zone}
89 };
90 
91 #define	MAX_PROPS	(sizeof (prop_table) / sizeof (prop_desc_t))
92 
93 dladm_status_t
94 dladm_set_prop(const char *link, const char *prop_name, char **prop_val,
95     uint_t val_cnt, uint_t flags, char **errprop)
96 {
97 	dladm_status_t		status = DLADM_STATUS_BADARG;
98 
99 	if (link == NULL || (prop_val == NULL && val_cnt > 0) ||
100 	    (prop_val != NULL && val_cnt == 0) || flags == 0)
101 		return (DLADM_STATUS_BADARG);
102 
103 	if ((flags & DLADM_OPT_TEMP) != 0) {
104 		status = i_dladm_set_prop_temp(link, prop_name, prop_val,
105 		    val_cnt, flags, errprop);
106 		if (status == DLADM_STATUS_TEMPONLY &&
107 		    (flags & DLADM_OPT_PERSIST) != 0)
108 			return (DLADM_STATUS_TEMPONLY);
109 
110 		if (status == DLADM_STATUS_NOTFOUND) {
111 			status = DLADM_STATUS_BADARG;
112 			if (dladm_wlan_is_valid(link)) {
113 				status = dladm_wlan_set_prop(link, prop_name,
114 				    prop_val, val_cnt, errprop);
115 			}
116 		}
117 		if (status != DLADM_STATUS_OK)
118 			return (status);
119 	}
120 	if ((flags & DLADM_OPT_PERSIST) != 0) {
121 		if (i_dladm_is_prop_temponly(prop_name, errprop))
122 			return (DLADM_STATUS_TEMPONLY);
123 
124 		status = i_dladm_set_prop_db(link, prop_name,
125 		    prop_val, val_cnt);
126 	}
127 	return (status);
128 }
129 
130 dladm_status_t
131 dladm_walk_prop(const char *link, void *arg,
132     boolean_t (*func)(void *, const char *))
133 {
134 	int	i;
135 
136 	if (link == NULL || func == NULL)
137 		return (DLADM_STATUS_BADARG);
138 
139 	/* For wifi links, show wifi properties first */
140 	if (dladm_wlan_is_valid(link)) {
141 		dladm_status_t	status;
142 
143 		status = dladm_wlan_walk_prop(link, arg, func);
144 		if (status != DLADM_STATUS_OK)
145 			return (status);
146 	}
147 
148 	/* Then show data-link properties if there are any */
149 	for (i = 0; i < MAX_PROPS; i++) {
150 		if (!func(arg, prop_table[i].pd_name))
151 			break;
152 	}
153 	return (DLADM_STATUS_OK);
154 }
155 
156 dladm_status_t
157 dladm_get_prop(const char *link, dladm_prop_type_t type,
158     const char *prop_name, char **prop_val, uint_t *val_cntp)
159 {
160 	dladm_status_t status;
161 
162 	if (link == NULL || prop_name == NULL || prop_val == NULL ||
163 	    val_cntp == NULL || *val_cntp == 0)
164 		return (DLADM_STATUS_BADARG);
165 
166 	if (type == DLADM_PROP_VAL_PERSISTENT) {
167 		if (i_dladm_is_prop_temponly(prop_name, NULL))
168 			return (DLADM_STATUS_TEMPONLY);
169 		return (i_dladm_get_prop_db(link, prop_name,
170 		    prop_val, val_cntp));
171 	}
172 
173 	status = i_dladm_get_prop_temp(link, type, prop_name,
174 	    prop_val, val_cntp);
175 	if (status != DLADM_STATUS_NOTFOUND)
176 		return (status);
177 
178 	if (dladm_wlan_is_valid(link)) {
179 		return (dladm_wlan_get_prop(link, type, prop_name,
180 		    prop_val, val_cntp));
181 	}
182 	return (DLADM_STATUS_BADARG);
183 }
184 
185 /*
186  * Data structures used for implementing persistent link properties
187  */
188 typedef struct linkprop_val {
189 	const char		*lv_name;
190 	struct linkprop_val	*lv_nextval;
191 } linkprop_val_t;
192 
193 typedef struct linkprop_info {
194 	const char		*li_name;
195 	struct linkprop_info	*li_nextprop;
196 	struct linkprop_val	*li_val;
197 } linkprop_info_t;
198 
199 typedef struct linkprop_db_state	linkprop_db_state_t;
200 
201 typedef boolean_t (*linkprop_db_op_t)(linkprop_db_state_t *,
202     char *, linkprop_info_t *, dladm_status_t *);
203 
204 struct linkprop_db_state {
205 	linkprop_db_op_t	ls_op;
206 	const char		*ls_link;
207 	const char		*ls_propname;
208 	char			**ls_propval;
209 	uint_t			*ls_valcntp;
210 };
211 
212 static void
213 free_linkprops(linkprop_info_t *lip)
214 {
215 	linkprop_info_t	*lip_next;
216 	linkprop_val_t	*lvp, *lvp_next;
217 
218 	for (; lip != NULL; lip = lip_next) {
219 		lip_next = lip->li_nextprop;
220 		for (lvp = lip->li_val; lvp != NULL; lvp = lvp_next) {
221 			lvp_next = lvp->lv_nextval;
222 			free(lvp);
223 		}
224 		free(lip);
225 	}
226 }
227 
228 /*
229  * Generate an entry in the link property database.
230  * Each entry has this format:
231  * <linkname>	<prop0>=<val0>,...,<valn>;...;<propn>=<val0>,...,<valn>;
232  */
233 static void
234 generate_linkprop_line(linkprop_db_state_t *lsp, char *buf,
235     linkprop_info_t *listp, dladm_status_t *statusp)
236 {
237 	char		tmpbuf[MAXLINELEN];
238 	char		*ptr, *lim = tmpbuf + MAXLINELEN;
239 	linkprop_info_t	*lip = listp;
240 	linkprop_val_t	*lvp = NULL;
241 
242 	/*
243 	 * Delete line if there are no properties left.
244 	 */
245 	if (lip == NULL ||
246 	    (lip->li_val == NULL && lip->li_nextprop == NULL)) {
247 		buf[0] = '\0';
248 		return;
249 	}
250 	ptr = tmpbuf;
251 	ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", lsp->ls_link);
252 	for (; lip != NULL; lip = lip->li_nextprop) {
253 		/*
254 		 * Skip properties without values.
255 		 */
256 		if (lip->li_val == NULL)
257 			continue;
258 
259 		ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s=", lip->li_name);
260 		for (lvp = lip->li_val; lvp != NULL; lvp = lvp->lv_nextval) {
261 			ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s%c",
262 			    lvp->lv_name,
263 			    ((lvp->lv_nextval == NULL) ? ';' : ','));
264 		}
265 	}
266 	if (ptr > lim) {
267 		*statusp = DLADM_STATUS_TOOSMALL;
268 		return;
269 	}
270 	(void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf);
271 }
272 
273 /*
274  * This function is used to update or create an entry in the persistent db.
275  * process_linkprop_db() will first scan the db for an entry matching the
276  * specified link. If a match is found, this function is invoked with the
277  * entry's contents (buf) and its linked-list representation (listp). lsp
278  * holds the name and values of the property to be added or updated; this
279  * information will be merged with listp. Subsequently, an updated entry
280  * will be written to buf, which will in turn be written to disk by
281  * process_linkprop_db(). If no entry matches the specified link, listp
282  * will be NULL; a new entry will be generated in this case and it will
283  * contain only the property information in lsp.
284  */
285 static boolean_t
286 process_linkprop_set(linkprop_db_state_t *lsp, char *buf,
287     linkprop_info_t *listp, dladm_status_t *statusp)
288 {
289 	dladm_status_t	status;
290 	linkprop_info_t	*lastp = NULL, *lip = listp, *nlip = NULL;
291 	linkprop_val_t	**lvpp;
292 	int		i;
293 
294 	if (lsp->ls_propname == NULL) {
295 		buf[0] = '\0';
296 		return (B_FALSE);
297 	}
298 
299 	/*
300 	 * Find the linkprop we want to change.
301 	 */
302 	for (; lip != NULL; lip = lip->li_nextprop) {
303 		if (strcmp(lip->li_name, lsp->ls_propname) == 0)
304 			break;
305 
306 		lastp = lip;
307 	}
308 
309 	if (lip == NULL) {
310 		/*
311 		 * If the linkprop is not found, append it to the list.
312 		 */
313 		if ((nlip = malloc(sizeof (linkprop_info_t))) == NULL) {
314 			status = DLADM_STATUS_NOMEM;
315 			goto fail;
316 		}
317 		/*
318 		 * nlip will need to be freed later if there is no list to
319 		 * append to.
320 		 */
321 		if (lastp != NULL)
322 			lastp->li_nextprop = nlip;
323 		nlip->li_name = lsp->ls_propname;
324 		nlip->li_nextprop = NULL;
325 		nlip->li_val = NULL;
326 		lvpp = &nlip->li_val;
327 	} else {
328 		linkprop_val_t	*lvp, *lvp_next;
329 
330 		/*
331 		 * If the linkprop is found, delete the existing values from it.
332 		 */
333 		for (lvp = lip->li_val; lvp != NULL; lvp = lvp_next) {
334 			lvp_next = lvp->lv_nextval;
335 			free(lvp);
336 		}
337 		lip->li_val = NULL;
338 		lvpp = &lip->li_val;
339 	}
340 
341 	/*
342 	 * Fill our linkprop with the specified values.
343 	 */
344 	for (i = 0; i < *lsp->ls_valcntp; i++) {
345 		if ((*lvpp = malloc(sizeof (linkprop_val_t))) == NULL) {
346 			status = DLADM_STATUS_NOMEM;
347 			goto fail;
348 		}
349 		(*lvpp)->lv_name = lsp->ls_propval[i];
350 		(*lvpp)->lv_nextval = NULL;
351 		lvpp = &(*lvpp)->lv_nextval;
352 	}
353 
354 	if (listp != NULL) {
355 		generate_linkprop_line(lsp, buf, listp, statusp);
356 	} else {
357 		generate_linkprop_line(lsp, buf, nlip, statusp);
358 		free_linkprops(nlip);
359 	}
360 	return (B_FALSE);
361 
362 fail:
363 	*statusp = status;
364 	if (listp == NULL)
365 		free_linkprops(nlip);
366 
367 	return (B_FALSE);
368 }
369 
370 /*
371  * This function is used for retrieving the values for a specific property.
372  * It gets called if an entry matching the specified link exists in the db.
373  * The entry is converted into a linked-list listp. This list is then scanned
374  * for the specified property name; if a matching property exists, its
375  * associated values are copied to the array lsp->ls_propval.
376  */
377 /* ARGSUSED */
378 static boolean_t
379 process_linkprop_get(linkprop_db_state_t *lsp, char *buf,
380     linkprop_info_t *listp, dladm_status_t *statusp)
381 {
382 	linkprop_info_t	*lip = listp;
383 	linkprop_val_t	*lvp;
384 	uint_t		valcnt = 0;
385 
386 	/*
387 	 * Find the linkprop we want to get.
388 	 */
389 	for (; lip != NULL; lip = lip->li_nextprop) {
390 		if (strcmp(lip->li_name, lsp->ls_propname) == 0)
391 			break;
392 	}
393 	if (lip == NULL) {
394 		*statusp = DLADM_STATUS_NOTFOUND;
395 		return (B_FALSE);
396 	}
397 
398 	for (lvp = lip->li_val; lvp != NULL; lvp = lvp->lv_nextval) {
399 		(void) strncpy(lsp->ls_propval[valcnt], lvp->lv_name,
400 		    DLADM_PROP_VAL_MAX);
401 
402 		if (++valcnt >= *lsp->ls_valcntp && lvp->lv_nextval != NULL) {
403 			*statusp = DLADM_STATUS_TOOSMALL;
404 			return (B_FALSE);
405 		}
406 	}
407 	/*
408 	 * This function is meant to be called at most once for each call
409 	 * to process_linkprop_db(). For this reason, it's ok to overwrite
410 	 * the caller's valcnt array size with the actual number of values
411 	 * returned.
412 	 */
413 	*lsp->ls_valcntp = valcnt;
414 	return (B_FALSE);
415 }
416 
417 /*
418  * This is used for initializing link properties.
419  * Unlike the other routines, this gets called for every entry in the
420  * database. lsp->ls_link is not user-specified but instead is set to
421  * the current link being processed.
422  */
423 /* ARGSUSED */
424 static boolean_t
425 process_linkprop_init(linkprop_db_state_t *lsp, char *buf,
426     linkprop_info_t *listp, dladm_status_t *statusp)
427 {
428 	dladm_status_t	status = DLADM_STATUS_OK;
429 	linkprop_info_t	*lip = listp;
430 	linkprop_val_t	*lvp;
431 	uint_t		valcnt, i;
432 	char		**propval;
433 
434 	for (; lip != NULL; lip = lip->li_nextprop) {
435 		/*
436 		 * Construct the propval array and fill it with
437 		 * values from listp.
438 		 */
439 		for (lvp = lip->li_val, valcnt = 0;
440 		    lvp != NULL; lvp = lvp->lv_nextval, valcnt++);
441 
442 		propval = malloc(sizeof (char *) * valcnt);
443 		if (propval == NULL) {
444 			*statusp = DLADM_STATUS_NOMEM;
445 			break;
446 		}
447 		lvp = lip->li_val;
448 		for (i = 0; i < valcnt; i++, lvp = lvp->lv_nextval)
449 			propval[i] = (char *)lvp->lv_name;
450 
451 		status = dladm_set_prop(lsp->ls_link, lip->li_name,
452 		    propval, valcnt, DLADM_OPT_TEMP, NULL);
453 
454 		/*
455 		 * We continue with initializing other properties even
456 		 * after encountering an error. This error will be
457 		 * propagated to the caller via 'statusp'.
458 		 */
459 		if (status != DLADM_STATUS_OK)
460 			*statusp = status;
461 
462 		free(propval);
463 	}
464 	return (B_TRUE);
465 }
466 
467 static int
468 parse_linkprops(char *buf, linkprop_info_t **lipp)
469 {
470 	int			i, len;
471 	char			*curr;
472 	linkprop_info_t		*lip = NULL;
473 	linkprop_info_t		**tailp = lipp;
474 	linkprop_val_t		*lvp = NULL;
475 	linkprop_val_t		**vtailp = NULL;
476 
477 	curr = buf;
478 	len = strlen(buf);
479 	for (i = 0; i < len; i++) {
480 		char		c = buf[i];
481 		boolean_t	match = (c == '=' || c == ',' || c == ';');
482 
483 		/*
484 		 * Move to the next character if there is no match and
485 		 * if we have not reached the last character.
486 		 */
487 		if (!match && i != len - 1)
488 			continue;
489 
490 		if (match) {
491 			/*
492 			 * Nul-terminate the string pointed to by 'curr'.
493 			 */
494 			buf[i] = '\0';
495 			if (*curr == '\0')
496 				goto fail;
497 		}
498 
499 		if (lip != NULL) {
500 			/*
501 			 * We get here after we have processed the "<prop>="
502 			 * pattern. The pattern we are now interested in is
503 			 * "<val0>,<val1>,...,<valn>;". For each value we
504 			 * find, a linkprop_val_t will be allocated and
505 			 * added to the current 'lip'.
506 			 */
507 			if (c == '=')
508 				goto fail;
509 
510 			lvp = malloc(sizeof (*lvp));
511 			if (lvp == NULL)
512 				goto fail;
513 
514 			lvp->lv_name = curr;
515 			lvp->lv_nextval = NULL;
516 			*vtailp = lvp;
517 			vtailp = &lvp->lv_nextval;
518 
519 			if (c == ';') {
520 				tailp = &lip->li_nextprop;
521 				vtailp = NULL;
522 				lip = NULL;
523 			}
524 		} else {
525 			/*
526 			 * lip == NULL indicates that 'curr' must be refering
527 			 * to a property name. We allocate a new linkprop_info_t
528 			 * append it to the list given by the caller.
529 			 */
530 			if (c != '=')
531 				goto fail;
532 
533 			lip = malloc(sizeof (*lip));
534 			if (lip == NULL)
535 				goto fail;
536 
537 			lip->li_name = curr;
538 			lip->li_val = NULL;
539 			lip->li_nextprop = NULL;
540 			*tailp = lip;
541 			vtailp = &lip->li_val;
542 		}
543 		curr = buf + i + 1;
544 	}
545 	/*
546 	 * The list must be non-empty and the last character must be ';'.
547 	 */
548 	if (*lipp == NULL || lip != NULL)
549 		goto fail;
550 
551 	return (0);
552 
553 fail:
554 	free_linkprops(*lipp);
555 	*lipp = NULL;
556 	return (-1);
557 }
558 
559 static boolean_t
560 process_linkprop_line(linkprop_db_state_t *lsp, char *buf,
561     dladm_status_t *statusp)
562 {
563 	linkprop_info_t		*lip = NULL;
564 	int			i, len, llen;
565 	char			*str, *lasts;
566 	boolean_t		cont, nolink = B_FALSE;
567 
568 	/*
569 	 * Skip leading spaces, blank lines, and comments.
570 	 */
571 	len = strlen(buf);
572 	for (i = 0; i < len; i++) {
573 		if (!isspace(buf[i]))
574 			break;
575 	}
576 	if (i == len || buf[i] == '#')
577 		return (B_TRUE);
578 
579 	str = buf + i;
580 	if (lsp->ls_link != NULL) {
581 		/*
582 		 * Skip links we're not interested in.
583 		 * Note that strncmp() and isspace() are used here
584 		 * instead of strtok() and strcmp() because we don't
585 		 * want to modify buf in case it does not contain the
586 		 * specified link.
587 		 */
588 		llen = strlen(lsp->ls_link);
589 		if (strncmp(str, lsp->ls_link, llen) != 0 ||
590 		    !isspace(str[llen]))
591 			return (B_TRUE);
592 	} else {
593 		/*
594 		 * If a link is not specified, find the link name
595 		 * and assign it to lsp->ls_link.
596 		 */
597 		if (strtok_r(str, " \n\t", &lasts) == NULL)
598 			goto fail;
599 
600 		llen = strlen(str);
601 		lsp->ls_link = str;
602 		nolink = B_TRUE;
603 	}
604 	str += llen + 1;
605 	if (str >= buf + len)
606 		goto fail;
607 
608 	/*
609 	 * Now find the list of link properties.
610 	 */
611 	if ((str = strtok_r(str, " \n\t", &lasts)) == NULL)
612 		goto fail;
613 
614 	if (parse_linkprops(str, &lip) < 0)
615 		goto fail;
616 
617 	cont = (*lsp->ls_op)(lsp, buf, lip, statusp);
618 	free_linkprops(lip);
619 	if (nolink)
620 		lsp->ls_link = NULL;
621 	return (cont);
622 
623 fail:
624 	free_linkprops(lip);
625 	if (nolink)
626 		lsp->ls_link = NULL;
627 
628 	/*
629 	 * Delete corrupted line.
630 	 */
631 	buf[0] = '\0';
632 	return (B_TRUE);
633 }
634 
635 static dladm_status_t
636 process_linkprop_db(void *arg, FILE *fp, FILE *nfp)
637 {
638 	linkprop_db_state_t	*lsp = arg;
639 	dladm_status_t		status = DLADM_STATUS_OK;
640 	char			buf[MAXLINELEN];
641 	boolean_t		cont = B_TRUE;
642 
643 	/*
644 	 * This loop processes each line of the configuration file.
645 	 * buf can potentially be modified by process_linkprop_line().
646 	 * If this is a write operation and buf is not truncated, buf will
647 	 * be written to disk. process_linkprop_line() will no longer be
648 	 * called after it returns B_FALSE; at which point the remainder
649 	 * of the file will continue to be read and, if necessary, written
650 	 * to disk as well.
651 	 */
652 	while (fgets(buf, MAXLINELEN, fp) != NULL) {
653 		if (cont)
654 			cont = process_linkprop_line(lsp, buf, &status);
655 
656 		if (nfp != NULL && buf[0] != '\0' && fputs(buf, nfp) == EOF) {
657 			status = dladm_errno2status(errno);
658 			break;
659 		}
660 	}
661 
662 	if (status != DLADM_STATUS_OK || !cont)
663 		return (status);
664 
665 	if (lsp->ls_op == process_linkprop_set) {
666 		/*
667 		 * If the specified link is not found above, we add the
668 		 * link and its properties to the configuration file.
669 		 */
670 		(void) (*lsp->ls_op)(lsp, buf, NULL, &status);
671 		if (status == DLADM_STATUS_OK && fputs(buf, nfp) == EOF)
672 			status = dladm_errno2status(errno);
673 	}
674 
675 	if (lsp->ls_op == process_linkprop_get)
676 		status = DLADM_STATUS_NOTFOUND;
677 
678 	return (status);
679 }
680 
681 #define	LINKPROP_RW_DB(statep, writeop) \
682 	(i_dladm_rw_db("/etc/dladm/linkprop.conf", \
683 	S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, process_linkprop_db, \
684 	(statep), (writeop)))
685 
686 static dladm_status_t
687 i_dladm_set_prop_db(const char *link, const char *prop_name,
688     char **prop_val, uint_t val_cnt)
689 {
690 	linkprop_db_state_t	state;
691 
692 	state.ls_op = process_linkprop_set;
693 	state.ls_link = link;
694 	state.ls_propname = prop_name;
695 	state.ls_propval = prop_val;
696 	state.ls_valcntp = &val_cnt;
697 
698 	return (LINKPROP_RW_DB(&state, B_TRUE));
699 }
700 
701 static dladm_status_t
702 i_dladm_get_prop_db(const char *link, const char *prop_name,
703     char **prop_val, uint_t *val_cntp)
704 {
705 	linkprop_db_state_t	state;
706 
707 	state.ls_op = process_linkprop_get;
708 	state.ls_link = link;
709 	state.ls_propname = prop_name;
710 	state.ls_propval = prop_val;
711 	state.ls_valcntp = val_cntp;
712 
713 	return (LINKPROP_RW_DB(&state, B_FALSE));
714 }
715 
716 dladm_status_t
717 dladm_init_linkprop(void)
718 {
719 	linkprop_db_state_t	state;
720 
721 	state.ls_op = process_linkprop_init;
722 	state.ls_link = NULL;
723 	state.ls_propname = NULL;
724 	state.ls_propval = NULL;
725 	state.ls_valcntp = NULL;
726 
727 	return (LINKPROP_RW_DB(&state, B_FALSE));
728 }
729 
730 static dladm_status_t
731 i_dladm_get_zoneid(const char *link, zoneid_t *zidp)
732 {
733 	int fd;
734 	dld_hold_vlan_t	dhv;
735 
736 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
737 		return (dladm_errno2status(errno));
738 
739 	bzero(&dhv, sizeof (dld_hold_vlan_t));
740 	(void) strlcpy(dhv.dhv_name, link, IFNAMSIZ);
741 	dhv.dhv_zid = -1;
742 
743 	if (i_dladm_ioctl(fd, DLDIOCZIDGET, &dhv, sizeof (dhv)) < 0 &&
744 	    errno != ENOENT) {
745 		dladm_status_t status = dladm_errno2status(errno);
746 
747 		(void) close(fd);
748 		return (status);
749 	}
750 
751 	if (errno == ENOENT)
752 		*zidp = GLOBAL_ZONEID;
753 	else
754 		*zidp = dhv.dhv_zid;
755 
756 	(void) close(fd);
757 	return (DLADM_STATUS_OK);
758 }
759 
760 typedef int (*zone_get_devroot_t)(char *, char *, size_t);
761 
762 static int
763 i_dladm_get_zone_dev(char *zone_name, char *dev, size_t devlen)
764 {
765 	char			root[MAXPATHLEN];
766 	zone_get_devroot_t	real_zone_get_devroot;
767 	void			*dlhandle;
768 	void			*sym;
769 	int			ret;
770 
771 	if ((dlhandle = dlopen("libzonecfg.so.1", RTLD_LAZY)) == NULL)
772 		return (-1);
773 
774 	if ((sym = dlsym(dlhandle, "zone_get_devroot")) == NULL) {
775 		(void) dlclose(dlhandle);
776 		return (-1);
777 	}
778 
779 	real_zone_get_devroot = (zone_get_devroot_t)sym;
780 
781 	if ((ret = real_zone_get_devroot(zone_name, root, sizeof (root))) == 0)
782 		(void) snprintf(dev, devlen, "%s%s", root, "/dev");
783 	(void) dlclose(dlhandle);
784 	return (ret);
785 }
786 
787 static dladm_status_t
788 i_dladm_add_deventry(zoneid_t zid, const char *link)
789 {
790 	char		path[MAXPATHLEN];
791 	di_prof_t	prof = NULL;
792 	char		zone_name[ZONENAME_MAX];
793 	dladm_status_t	status;
794 
795 	if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0)
796 		return (dladm_errno2status(errno));
797 	if (i_dladm_get_zone_dev(zone_name, path, sizeof (path)) != 0)
798 		return (dladm_errno2status(errno));
799 	if (di_prof_init(path, &prof) != 0)
800 		return (dladm_errno2status(errno));
801 
802 	status = DLADM_STATUS_OK;
803 	if (di_prof_add_dev(prof, link) != 0) {
804 		status = dladm_errno2status(errno);
805 		goto cleanup;
806 	}
807 	if (di_prof_commit(prof) != 0)
808 		status = dladm_errno2status(errno);
809 cleanup:
810 	if (prof)
811 		di_prof_fini(prof);
812 
813 	return (status);
814 }
815 
816 static dladm_status_t
817 i_dladm_remove_deventry(zoneid_t zid, const char *link)
818 {
819 	char		path[MAXPATHLEN];
820 	di_prof_t	prof = NULL;
821 	char		zone_name[ZONENAME_MAX];
822 	dladm_status_t	status;
823 
824 	if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0)
825 		return (dladm_errno2status(errno));
826 	if (i_dladm_get_zone_dev(zone_name, path, sizeof (path)) != 0)
827 		return (dladm_errno2status(errno));
828 	if (di_prof_init(path, &prof) != 0)
829 		return (dladm_errno2status(errno));
830 
831 	status = DLADM_STATUS_OK;
832 	if (di_prof_add_exclude(prof, link) != 0) {
833 		status = dladm_errno2status(errno);
834 		goto cleanup;
835 	}
836 	if (di_prof_commit(prof) != 0)
837 		status = dladm_errno2status(errno);
838 cleanup:
839 	if (prof)
840 		di_prof_fini(prof);
841 
842 	return (status);
843 }
844 
845 static dladm_status_t
846 do_get_zone(const char *link, char **prop_val, uint_t *val_cnt)
847 {
848 	char		zone_name[ZONENAME_MAX];
849 	zoneid_t	zid;
850 	dladm_status_t	status;
851 
852 	status = i_dladm_get_zoneid(link, &zid);
853 	if (status != DLADM_STATUS_OK)
854 		return (status);
855 
856 	*val_cnt = 1;
857 	if (zid != GLOBAL_ZONEID) {
858 		if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0)
859 			return (dladm_errno2status(errno));
860 
861 		(void) strncpy(*prop_val, zone_name, DLADM_PROP_VAL_MAX);
862 	} else {
863 		*prop_val[0] = '\0';
864 	}
865 
866 	return (DLADM_STATUS_OK);
867 }
868 
869 static dladm_status_t
870 do_set_zone(const char *link, val_desc_t *vdp, uint_t val_cnt)
871 {
872 	dladm_status_t	status;
873 	zoneid_t	zid_old, zid_new;
874 
875 	if (val_cnt != 1)
876 		return (DLADM_STATUS_BADVALCNT);
877 
878 	status = i_dladm_get_zoneid(link, &zid_old);
879 	if (status != DLADM_STATUS_OK)
880 		return (status);
881 
882 	/* Do nothing if setting to current value */
883 	zid_new = (zoneid_t)vdp->vd_val;
884 	if (zid_new == zid_old)
885 		return (DLADM_STATUS_OK);
886 
887 	if (zid_old != GLOBAL_ZONEID) {
888 		if (dladm_rele_link(link, GLOBAL_ZONEID, B_TRUE) < 0)
889 			return (dladm_errno2status(errno));
890 
891 		if (zone_remove_datalink(zid_old, (char *)link) != 0 &&
892 		    errno != ENXIO) {
893 			status = dladm_errno2status(errno);
894 			goto rollback1;
895 		}
896 
897 		status = i_dladm_remove_deventry(zid_old, link);
898 		if (status != DLADM_STATUS_OK)
899 			goto rollback2;
900 	}
901 
902 	if (zid_new != GLOBAL_ZONEID) {
903 		if (zone_add_datalink(zid_new, (char *)link) != 0) {
904 			status = dladm_errno2status(errno);
905 			goto rollback3;
906 		}
907 
908 		if (dladm_hold_link(link, zid_new, B_TRUE) < 0) {
909 			(void) zone_remove_datalink(zid_new, (char *)link);
910 			status = dladm_errno2status(errno);
911 			goto rollback3;
912 		}
913 
914 		status = i_dladm_add_deventry(zid_new, link);
915 		if (status != DLADM_STATUS_OK) {
916 			(void) dladm_rele_link(link, GLOBAL_ZONEID, B_FALSE);
917 			(void) zone_remove_datalink(zid_new, (char *)link);
918 			goto rollback3;
919 		}
920 	}
921 	return (DLADM_STATUS_OK);
922 
923 rollback3:
924 	if (zid_old != GLOBAL_ZONEID)
925 		(void) i_dladm_add_deventry(zid_old, link);
926 rollback2:
927 	if (zid_old != GLOBAL_ZONEID)
928 		(void) zone_add_datalink(zid_old, (char *)link);
929 rollback1:
930 	(void) dladm_hold_link(link, zid_old, B_FALSE);
931 cleanexit:
932 	return (status);
933 }
934 
935 /* ARGSUSED */
936 static dladm_status_t
937 do_check_zone(prop_desc_t *pdp, char **prop_val, uint_t val_cnt,
938     val_desc_t **vdpp)
939 {
940 	zoneid_t 	zid;
941 	val_desc_t	*vdp = NULL;
942 
943 	if (val_cnt != 1)
944 		return (DLADM_STATUS_BADVALCNT);
945 
946 	if ((zid = getzoneidbyname(*prop_val)) == -1)
947 		return (DLADM_STATUS_BADVAL);
948 
949 	if (zid != GLOBAL_ZONEID) {
950 		ushort_t	flags;
951 
952 		if (zone_getattr(zid, ZONE_ATTR_FLAGS, &flags,
953 		    sizeof (flags)) < 0) {
954 			return (dladm_errno2status(errno));
955 		}
956 
957 		if (!(flags & ZF_NET_EXCL)) {
958 			return (DLADM_STATUS_BADVAL);
959 		}
960 	}
961 
962 	vdp = malloc(sizeof (val_desc_t));
963 	if (vdp == NULL)
964 		return (DLADM_STATUS_NOMEM);
965 
966 	vdp->vd_val = (void *)zid;
967 	*vdpp = vdp;
968 	return (DLADM_STATUS_OK);
969 }
970 
971 static dladm_status_t
972 i_dladm_get_prop_temp(const char *link, dladm_prop_type_t type,
973     const char *prop_name, char **prop_val, uint_t *val_cntp)
974 {
975 	int 		i;
976 	dladm_status_t	status;
977 	uint_t		cnt;
978 	prop_desc_t	*pdp;
979 
980 	if (link == NULL || prop_name == NULL || prop_val == NULL ||
981 	    val_cntp == NULL || *val_cntp == 0)
982 		return (DLADM_STATUS_BADARG);
983 
984 	for (i = 0; i < MAX_PROPS; i++)
985 		if (strcasecmp(prop_name, prop_table[i].pd_name) == 0)
986 			break;
987 
988 	if (i == MAX_PROPS)
989 		return (DLADM_STATUS_NOTFOUND);
990 
991 	pdp = &prop_table[i];
992 	status = DLADM_STATUS_OK;
993 
994 	switch (type) {
995 	case DLADM_PROP_VAL_CURRENT:
996 		status = pdp->pd_get(link, prop_val, val_cntp);
997 		break;
998 	case DLADM_PROP_VAL_DEFAULT:
999 		if (pdp->pd_defval.vd_name == NULL) {
1000 			status = DLADM_STATUS_NOTSUP;
1001 			break;
1002 		}
1003 		(void) strcpy(*prop_val, pdp->pd_defval.vd_name);
1004 		*val_cntp = 1;
1005 		break;
1006 
1007 	case DLADM_PROP_VAL_MODIFIABLE:
1008 		if (pdp->pd_getmod != NULL) {
1009 			status = pdp->pd_getmod(link, prop_val, val_cntp);
1010 			break;
1011 		}
1012 		cnt = pdp->pd_nmodval;
1013 		if (cnt == 0) {
1014 			status = DLADM_STATUS_NOTSUP;
1015 		} else if (cnt > *val_cntp) {
1016 			status = DLADM_STATUS_TOOSMALL;
1017 		} else {
1018 			for (i = 0; i < cnt; i++) {
1019 				(void) strcpy(prop_val[i],
1020 				    pdp->pd_modval[i].vd_name);
1021 			}
1022 			*val_cntp = cnt;
1023 		}
1024 		break;
1025 	default:
1026 		status = DLADM_STATUS_BADARG;
1027 		break;
1028 	}
1029 
1030 	return (status);
1031 }
1032 
1033 static dladm_status_t
1034 i_dladm_set_one_prop_temp(const char *link, prop_desc_t *pdp, char **prop_val,
1035     uint_t val_cnt, uint_t flags)
1036 {
1037 	dladm_status_t	status;
1038 	val_desc_t	*vdp = NULL;
1039 	uint_t		cnt;
1040 
1041 	if (pdp->pd_temponly && (flags & DLADM_OPT_PERSIST) != 0)
1042 		return (DLADM_STATUS_TEMPONLY);
1043 
1044 	if (pdp->pd_set == NULL)
1045 		return (DLADM_STATUS_PROPRDONLY);
1046 
1047 	if (prop_val != NULL) {
1048 		if (pdp->pd_check != NULL)
1049 			status = pdp->pd_check(pdp, prop_val, val_cnt, &vdp);
1050 		else
1051 			status = DLADM_STATUS_BADARG;
1052 
1053 		if (status != DLADM_STATUS_OK)
1054 			return (status);
1055 
1056 		cnt = val_cnt;
1057 	} else {
1058 		if (pdp->pd_defval.vd_name == NULL)
1059 			return (DLADM_STATUS_NOTSUP);
1060 
1061 		if ((vdp = malloc(sizeof (val_desc_t))) == NULL)
1062 			return (DLADM_STATUS_NOMEM);
1063 
1064 		(void) memcpy(vdp, &pdp->pd_defval, sizeof (val_desc_t));
1065 		cnt = 1;
1066 	}
1067 
1068 	status = pdp->pd_set(link, vdp, cnt);
1069 
1070 	free(vdp);
1071 	return (status);
1072 }
1073 
1074 static dladm_status_t
1075 i_dladm_set_prop_temp(const char *link, const char *prop_name, char **prop_val,
1076     uint_t val_cnt, uint_t flags, char **errprop)
1077 {
1078 	int 		i;
1079 	dladm_status_t	status = DLADM_STATUS_OK;
1080 	boolean_t	found = B_FALSE;
1081 
1082 	for (i = 0; i < MAX_PROPS; i++) {
1083 		prop_desc_t	*pdp = &prop_table[i];
1084 		dladm_status_t	s;
1085 
1086 		if (prop_name != NULL &&
1087 		    (strcasecmp(prop_name, pdp->pd_name) != 0))
1088 			continue;
1089 
1090 		found = B_TRUE;
1091 		s = i_dladm_set_one_prop_temp(link, pdp, prop_val, val_cnt,
1092 		    flags);
1093 
1094 		if (prop_name != NULL) {
1095 			status = s;
1096 			break;
1097 		} else {
1098 			if (s != DLADM_STATUS_OK &&
1099 			    s != DLADM_STATUS_NOTSUP) {
1100 				if (errprop != NULL)
1101 					*errprop = pdp->pd_name;
1102 				status = s;
1103 				break;
1104 			}
1105 		}
1106 	}
1107 
1108 	if (!found)
1109 		status = DLADM_STATUS_NOTFOUND;
1110 
1111 	return (status);
1112 }
1113 
1114 static boolean_t
1115 i_dladm_is_prop_temponly(const char *prop_name, char **errprop)
1116 {
1117 	int 		i;
1118 
1119 	for (i = 0; i < MAX_PROPS; i++) {
1120 		prop_desc_t	*pdp = &prop_table[i];
1121 
1122 		if (prop_name != NULL &&
1123 		    (strcasecmp(prop_name, pdp->pd_name) != 0))
1124 			continue;
1125 
1126 		if (errprop != NULL)
1127 			*errprop = pdp->pd_name;
1128 
1129 		if (pdp->pd_temponly)
1130 			return (B_TRUE);
1131 	}
1132 
1133 	return (B_FALSE);
1134 }
1135