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 
443 		propval = malloc(sizeof (char *) * valcnt);
444 		if (propval == NULL) {
445 			*statusp = DLADM_STATUS_NOMEM;
446 			break;
447 		}
448 		lvp = lip->li_val;
449 		for (i = 0; i < valcnt; i++, lvp = lvp->lv_nextval)
450 			propval[i] = (char *)lvp->lv_name;
451 
452 		status = dladm_set_prop(lsp->ls_link, lip->li_name,
453 		    propval, valcnt, DLADM_OPT_TEMP, NULL);
454 
455 		/*
456 		 * We continue with initializing other properties even
457 		 * after encountering an error. This error will be
458 		 * propagated to the caller via 'statusp'.
459 		 */
460 		if (status != DLADM_STATUS_OK)
461 			*statusp = status;
462 
463 		free(propval);
464 	}
465 	return (B_TRUE);
466 }
467 
468 static int
469 parse_linkprops(char *buf, linkprop_info_t **lipp)
470 {
471 	int			i, len;
472 	char			*curr;
473 	linkprop_info_t		*lip = NULL;
474 	linkprop_info_t		**tailp = lipp;
475 	linkprop_val_t		*lvp = NULL;
476 	linkprop_val_t		**vtailp = NULL;
477 
478 	curr = buf;
479 	len = strlen(buf);
480 	for (i = 0; i < len; i++) {
481 		char		c = buf[i];
482 		boolean_t	match = (c == '=' || c == ',' || c == ';');
483 
484 		/*
485 		 * Move to the next character if there is no match and
486 		 * if we have not reached the last character.
487 		 */
488 		if (!match && i != len - 1)
489 			continue;
490 
491 		if (match) {
492 			/*
493 			 * Nul-terminate the string pointed to by 'curr'.
494 			 */
495 			buf[i] = '\0';
496 			if (*curr == '\0')
497 				goto fail;
498 		}
499 
500 		if (lip != NULL) {
501 			/*
502 			 * We get here after we have processed the "<prop>="
503 			 * pattern. The pattern we are now interested in is
504 			 * "<val0>,<val1>,...,<valn>;". For each value we
505 			 * find, a linkprop_val_t will be allocated and
506 			 * added to the current 'lip'.
507 			 */
508 			if (c == '=')
509 				goto fail;
510 
511 			lvp = malloc(sizeof (*lvp));
512 			if (lvp == NULL)
513 				goto fail;
514 
515 			lvp->lv_name = curr;
516 			lvp->lv_nextval = NULL;
517 			*vtailp = lvp;
518 			vtailp = &lvp->lv_nextval;
519 
520 			if (c == ';') {
521 				tailp = &lip->li_nextprop;
522 				vtailp = NULL;
523 				lip = NULL;
524 			}
525 		} else {
526 			/*
527 			 * lip == NULL indicates that 'curr' must be refering
528 			 * to a property name. We allocate a new linkprop_info_t
529 			 * append it to the list given by the caller.
530 			 */
531 			if (c != '=')
532 				goto fail;
533 
534 			lip = malloc(sizeof (*lip));
535 			if (lip == NULL)
536 				goto fail;
537 
538 			lip->li_name = curr;
539 			lip->li_val = NULL;
540 			lip->li_nextprop = NULL;
541 			*tailp = lip;
542 			vtailp = &lip->li_val;
543 		}
544 		curr = buf + i + 1;
545 	}
546 	/*
547 	 * The list must be non-empty and the last character must be ';'.
548 	 */
549 	if (*lipp == NULL || lip != NULL)
550 		goto fail;
551 
552 	return (0);
553 
554 fail:
555 	free_linkprops(*lipp);
556 	*lipp = NULL;
557 	return (-1);
558 }
559 
560 static boolean_t
561 process_linkprop_line(linkprop_db_state_t *lsp, char *buf,
562     dladm_status_t *statusp)
563 {
564 	linkprop_info_t		*lip = NULL;
565 	int			i, len, llen;
566 	char			*str, *lasts;
567 	boolean_t		cont, nolink = B_FALSE;
568 
569 	/*
570 	 * Skip leading spaces, blank lines, and comments.
571 	 */
572 	len = strlen(buf);
573 	for (i = 0; i < len; i++) {
574 		if (!isspace(buf[i]))
575 			break;
576 	}
577 	if (i == len || buf[i] == '#')
578 		return (B_TRUE);
579 
580 	str = buf + i;
581 	if (lsp->ls_link != NULL) {
582 		/*
583 		 * Skip links we're not interested in.
584 		 * Note that strncmp() and isspace() are used here
585 		 * instead of strtok() and strcmp() because we don't
586 		 * want to modify buf in case it does not contain the
587 		 * specified link.
588 		 */
589 		llen = strlen(lsp->ls_link);
590 		if (strncmp(str, lsp->ls_link, llen) != 0 ||
591 		    !isspace(str[llen]))
592 			return (B_TRUE);
593 	} else {
594 		/*
595 		 * If a link is not specified, find the link name
596 		 * and assign it to lsp->ls_link.
597 		 */
598 		if (strtok_r(str, " \n\t", &lasts) == NULL)
599 			goto fail;
600 
601 		llen = strlen(str);
602 		lsp->ls_link = str;
603 		nolink = B_TRUE;
604 	}
605 	str += llen + 1;
606 	if (str >= buf + len)
607 		goto fail;
608 
609 	/*
610 	 * Now find the list of link properties.
611 	 */
612 	if ((str = strtok_r(str, " \n\t", &lasts)) == NULL)
613 		goto fail;
614 
615 	if (parse_linkprops(str, &lip) < 0)
616 		goto fail;
617 
618 	cont = (*lsp->ls_op)(lsp, buf, lip, statusp);
619 	free_linkprops(lip);
620 	if (nolink)
621 		lsp->ls_link = NULL;
622 	return (cont);
623 
624 fail:
625 	free_linkprops(lip);
626 	if (nolink)
627 		lsp->ls_link = NULL;
628 
629 	/*
630 	 * Delete corrupted line.
631 	 */
632 	buf[0] = '\0';
633 	return (B_TRUE);
634 }
635 
636 static dladm_status_t
637 process_linkprop_db(void *arg, FILE *fp, FILE *nfp)
638 {
639 	linkprop_db_state_t	*lsp = arg;
640 	dladm_status_t		status = DLADM_STATUS_OK;
641 	char			buf[MAXLINELEN];
642 	boolean_t		cont = B_TRUE;
643 
644 	/*
645 	 * This loop processes each line of the configuration file.
646 	 * buf can potentially be modified by process_linkprop_line().
647 	 * If this is a write operation and buf is not truncated, buf will
648 	 * be written to disk. process_linkprop_line() will no longer be
649 	 * called after it returns B_FALSE; at which point the remainder
650 	 * of the file will continue to be read and, if necessary, written
651 	 * to disk as well.
652 	 */
653 	while (fgets(buf, MAXLINELEN, fp) != NULL) {
654 		if (cont)
655 			cont = process_linkprop_line(lsp, buf, &status);
656 
657 		if (nfp != NULL && buf[0] != '\0' && fputs(buf, nfp) == EOF) {
658 			status = dladm_errno2status(errno);
659 			break;
660 		}
661 	}
662 
663 	if (status != DLADM_STATUS_OK || !cont)
664 		return (status);
665 
666 	if (lsp->ls_op == process_linkprop_set) {
667 		/*
668 		 * If the specified link is not found above, we add the
669 		 * link and its properties to the configuration file.
670 		 */
671 		(void) (*lsp->ls_op)(lsp, buf, NULL, &status);
672 		if (status == DLADM_STATUS_OK && fputs(buf, nfp) == EOF)
673 			status = dladm_errno2status(errno);
674 	}
675 
676 	if (lsp->ls_op == process_linkprop_get)
677 		status = DLADM_STATUS_NOTFOUND;
678 
679 	return (status);
680 }
681 
682 #define	LINKPROP_RW_DB(statep, writeop) \
683 	(i_dladm_rw_db("/etc/dladm/linkprop.conf", \
684 	S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, process_linkprop_db, \
685 	(statep), (writeop)))
686 
687 static dladm_status_t
688 i_dladm_set_prop_db(const char *link, const char *prop_name,
689     char **prop_val, uint_t val_cnt)
690 {
691 	linkprop_db_state_t	state;
692 
693 	state.ls_op = process_linkprop_set;
694 	state.ls_link = link;
695 	state.ls_propname = prop_name;
696 	state.ls_propval = prop_val;
697 	state.ls_valcntp = &val_cnt;
698 
699 	return (LINKPROP_RW_DB(&state, B_TRUE));
700 }
701 
702 static dladm_status_t
703 i_dladm_get_prop_db(const char *link, const char *prop_name,
704     char **prop_val, uint_t *val_cntp)
705 {
706 	linkprop_db_state_t	state;
707 
708 	state.ls_op = process_linkprop_get;
709 	state.ls_link = link;
710 	state.ls_propname = prop_name;
711 	state.ls_propval = prop_val;
712 	state.ls_valcntp = val_cntp;
713 
714 	return (LINKPROP_RW_DB(&state, B_FALSE));
715 }
716 
717 dladm_status_t
718 dladm_init_linkprop(void)
719 {
720 	linkprop_db_state_t	state;
721 
722 	state.ls_op = process_linkprop_init;
723 	state.ls_link = NULL;
724 	state.ls_propname = NULL;
725 	state.ls_propval = NULL;
726 	state.ls_valcntp = NULL;
727 
728 	return (LINKPROP_RW_DB(&state, B_FALSE));
729 }
730 
731 static dladm_status_t
732 i_dladm_get_zoneid(const char *link, zoneid_t *zidp)
733 {
734 	int fd;
735 	dld_hold_vlan_t	dhv;
736 
737 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
738 		return (dladm_errno2status(errno));
739 
740 	bzero(&dhv, sizeof (dld_hold_vlan_t));
741 	(void) strlcpy(dhv.dhv_name, link, IFNAMSIZ);
742 	dhv.dhv_zid = -1;
743 
744 	if (i_dladm_ioctl(fd, DLDIOCZIDGET, &dhv, sizeof (dhv)) < 0 &&
745 	    errno != ENOENT) {
746 		dladm_status_t status = dladm_errno2status(errno);
747 
748 		(void) close(fd);
749 		return (status);
750 	}
751 
752 	if (errno == ENOENT)
753 		*zidp = GLOBAL_ZONEID;
754 	else
755 		*zidp = dhv.dhv_zid;
756 
757 	(void) close(fd);
758 	return (DLADM_STATUS_OK);
759 }
760 
761 typedef int (*zone_get_devroot_t)(char *, char *, size_t);
762 
763 static int
764 i_dladm_get_zone_dev(char *zone_name, char *dev, size_t devlen)
765 {
766 	char			root[MAXPATHLEN];
767 	zone_get_devroot_t	real_zone_get_devroot;
768 	void			*dlhandle;
769 	void			*sym;
770 	int			ret;
771 
772 	if ((dlhandle = dlopen("libzonecfg.so.1", RTLD_LAZY)) == NULL)
773 		return (-1);
774 
775 	if ((sym = dlsym(dlhandle, "zone_get_devroot")) == NULL) {
776 		(void) dlclose(dlhandle);
777 		return (-1);
778 	}
779 
780 	real_zone_get_devroot = (zone_get_devroot_t)sym;
781 
782 	if ((ret = real_zone_get_devroot(zone_name, root, sizeof (root))) == 0)
783 		(void) snprintf(dev, devlen, "%s%s", root, "/dev");
784 	(void) dlclose(dlhandle);
785 	return (ret);
786 }
787 
788 static dladm_status_t
789 i_dladm_add_deventry(zoneid_t zid, const char *link)
790 {
791 	char		path[MAXPATHLEN];
792 	di_prof_t	prof = NULL;
793 	char		zone_name[ZONENAME_MAX];
794 	dladm_status_t	status;
795 
796 	if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0)
797 		return (dladm_errno2status(errno));
798 	if (i_dladm_get_zone_dev(zone_name, path, sizeof (path)) != 0)
799 		return (dladm_errno2status(errno));
800 	if (di_prof_init(path, &prof) != 0)
801 		return (dladm_errno2status(errno));
802 
803 	status = DLADM_STATUS_OK;
804 	if (di_prof_add_dev(prof, link) != 0) {
805 		status = dladm_errno2status(errno);
806 		goto cleanup;
807 	}
808 	if (di_prof_commit(prof) != 0)
809 		status = dladm_errno2status(errno);
810 cleanup:
811 	if (prof)
812 		di_prof_fini(prof);
813 
814 	return (status);
815 }
816 
817 static dladm_status_t
818 i_dladm_remove_deventry(zoneid_t zid, const char *link)
819 {
820 	char		path[MAXPATHLEN];
821 	di_prof_t	prof = NULL;
822 	char		zone_name[ZONENAME_MAX];
823 	dladm_status_t	status;
824 
825 	if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0)
826 		return (dladm_errno2status(errno));
827 	if (i_dladm_get_zone_dev(zone_name, path, sizeof (path)) != 0)
828 		return (dladm_errno2status(errno));
829 	if (di_prof_init(path, &prof) != 0)
830 		return (dladm_errno2status(errno));
831 
832 	status = DLADM_STATUS_OK;
833 	if (di_prof_add_exclude(prof, link) != 0) {
834 		status = dladm_errno2status(errno);
835 		goto cleanup;
836 	}
837 	if (di_prof_commit(prof) != 0)
838 		status = dladm_errno2status(errno);
839 cleanup:
840 	if (prof)
841 		di_prof_fini(prof);
842 
843 	return (status);
844 }
845 
846 static dladm_status_t
847 do_get_zone(const char *link, char **prop_val, uint_t *val_cnt)
848 {
849 	char		zone_name[ZONENAME_MAX];
850 	zoneid_t	zid;
851 	dladm_status_t	status;
852 
853 	status = i_dladm_get_zoneid(link, &zid);
854 	if (status != DLADM_STATUS_OK)
855 		return (status);
856 
857 	*val_cnt = 1;
858 	if (zid != GLOBAL_ZONEID) {
859 		if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0)
860 			return (dladm_errno2status(errno));
861 
862 		(void) strncpy(*prop_val, zone_name, DLADM_PROP_VAL_MAX);
863 	} else {
864 		*prop_val[0] = '\0';
865 	}
866 
867 	return (DLADM_STATUS_OK);
868 }
869 
870 static dladm_status_t
871 do_set_zone(const char *link, val_desc_t *vdp, uint_t val_cnt)
872 {
873 	dladm_status_t	status;
874 	zoneid_t	zid_old, zid_new;
875 
876 	if (val_cnt != 1)
877 		return (DLADM_STATUS_BADVALCNT);
878 
879 	status = i_dladm_get_zoneid(link, &zid_old);
880 	if (status != DLADM_STATUS_OK)
881 		return (status);
882 
883 	/* Do nothing if setting to current value */
884 	zid_new = (intptr_t)(void *)vdp->vd_val;
885 	if (zid_new == zid_old)
886 		return (DLADM_STATUS_OK);
887 
888 	if (zid_old != GLOBAL_ZONEID) {
889 		if (dladm_rele_link(link, GLOBAL_ZONEID, B_TRUE) < 0)
890 			return (dladm_errno2status(errno));
891 
892 		if (zone_remove_datalink(zid_old, (char *)link) != 0 &&
893 		    errno != ENXIO) {
894 			status = dladm_errno2status(errno);
895 			goto rollback1;
896 		}
897 
898 		status = i_dladm_remove_deventry(zid_old, link);
899 		if (status != DLADM_STATUS_OK)
900 			goto rollback2;
901 	}
902 
903 	if (zid_new != GLOBAL_ZONEID) {
904 		if (zone_add_datalink(zid_new, (char *)link) != 0) {
905 			status = dladm_errno2status(errno);
906 			goto rollback3;
907 		}
908 
909 		if (dladm_hold_link(link, zid_new, B_TRUE) < 0) {
910 			(void) zone_remove_datalink(zid_new, (char *)link);
911 			status = dladm_errno2status(errno);
912 			goto rollback3;
913 		}
914 
915 		status = i_dladm_add_deventry(zid_new, link);
916 		if (status != DLADM_STATUS_OK) {
917 			(void) dladm_rele_link(link, GLOBAL_ZONEID, B_FALSE);
918 			(void) zone_remove_datalink(zid_new, (char *)link);
919 			goto rollback3;
920 		}
921 	}
922 	return (DLADM_STATUS_OK);
923 
924 rollback3:
925 	if (zid_old != GLOBAL_ZONEID)
926 		(void) i_dladm_add_deventry(zid_old, link);
927 rollback2:
928 	if (zid_old != GLOBAL_ZONEID)
929 		(void) zone_add_datalink(zid_old, (char *)link);
930 rollback1:
931 	(void) dladm_hold_link(link, zid_old, B_FALSE);
932 cleanexit:
933 	return (status);
934 }
935 
936 /* ARGSUSED */
937 static dladm_status_t
938 do_check_zone(prop_desc_t *pdp, char **prop_val, uint_t val_cnt,
939     val_desc_t **vdpp)
940 {
941 	zoneid_t 	zid;
942 	val_desc_t	*vdp = NULL;
943 
944 	if (val_cnt != 1)
945 		return (DLADM_STATUS_BADVALCNT);
946 
947 	if ((zid = getzoneidbyname(*prop_val)) == -1)
948 		return (DLADM_STATUS_BADVAL);
949 
950 	if (zid != GLOBAL_ZONEID) {
951 		ushort_t	flags;
952 
953 		if (zone_getattr(zid, ZONE_ATTR_FLAGS, &flags,
954 		    sizeof (flags)) < 0) {
955 			return (dladm_errno2status(errno));
956 		}
957 
958 		if (!(flags & ZF_NET_EXCL)) {
959 			return (DLADM_STATUS_BADVAL);
960 		}
961 	}
962 
963 	vdp = malloc(sizeof (val_desc_t));
964 	if (vdp == NULL)
965 		return (DLADM_STATUS_NOMEM);
966 
967 	vdp->vd_val = (void *)(uintptr_t)zid;
968 	*vdpp = vdp;
969 	return (DLADM_STATUS_OK);
970 }
971 
972 static dladm_status_t
973 i_dladm_get_prop_temp(const char *link, dladm_prop_type_t type,
974     const char *prop_name, char **prop_val, uint_t *val_cntp)
975 {
976 	int 		i;
977 	dladm_status_t	status;
978 	uint_t		cnt;
979 	prop_desc_t	*pdp;
980 
981 	if (link == NULL || prop_name == NULL || prop_val == NULL ||
982 	    val_cntp == NULL || *val_cntp == 0)
983 		return (DLADM_STATUS_BADARG);
984 
985 	for (i = 0; i < MAX_PROPS; i++)
986 		if (strcasecmp(prop_name, prop_table[i].pd_name) == 0)
987 			break;
988 
989 	if (i == MAX_PROPS)
990 		return (DLADM_STATUS_NOTFOUND);
991 
992 	pdp = &prop_table[i];
993 	status = DLADM_STATUS_OK;
994 
995 	switch (type) {
996 	case DLADM_PROP_VAL_CURRENT:
997 		status = pdp->pd_get(link, prop_val, val_cntp);
998 		break;
999 	case DLADM_PROP_VAL_DEFAULT:
1000 		if (pdp->pd_defval.vd_name == NULL) {
1001 			status = DLADM_STATUS_NOTSUP;
1002 			break;
1003 		}
1004 		(void) strcpy(*prop_val, pdp->pd_defval.vd_name);
1005 		*val_cntp = 1;
1006 		break;
1007 
1008 	case DLADM_PROP_VAL_MODIFIABLE:
1009 		if (pdp->pd_getmod != NULL) {
1010 			status = pdp->pd_getmod(link, prop_val, val_cntp);
1011 			break;
1012 		}
1013 		cnt = pdp->pd_nmodval;
1014 		if (cnt == 0) {
1015 			status = DLADM_STATUS_NOTSUP;
1016 		} else if (cnt > *val_cntp) {
1017 			status = DLADM_STATUS_TOOSMALL;
1018 		} else {
1019 			for (i = 0; i < cnt; i++) {
1020 				(void) strcpy(prop_val[i],
1021 				    pdp->pd_modval[i].vd_name);
1022 			}
1023 			*val_cntp = cnt;
1024 		}
1025 		break;
1026 	default:
1027 		status = DLADM_STATUS_BADARG;
1028 		break;
1029 	}
1030 
1031 	return (status);
1032 }
1033 
1034 static dladm_status_t
1035 i_dladm_set_one_prop_temp(const char *link, prop_desc_t *pdp, char **prop_val,
1036     uint_t val_cnt, uint_t flags)
1037 {
1038 	dladm_status_t	status;
1039 	val_desc_t	*vdp = NULL;
1040 	uint_t		cnt;
1041 
1042 	if (pdp->pd_temponly && (flags & DLADM_OPT_PERSIST) != 0)
1043 		return (DLADM_STATUS_TEMPONLY);
1044 
1045 	if (pdp->pd_set == NULL)
1046 		return (DLADM_STATUS_PROPRDONLY);
1047 
1048 	if (prop_val != NULL) {
1049 		if (pdp->pd_check != NULL)
1050 			status = pdp->pd_check(pdp, prop_val, val_cnt, &vdp);
1051 		else
1052 			status = DLADM_STATUS_BADARG;
1053 
1054 		if (status != DLADM_STATUS_OK)
1055 			return (status);
1056 
1057 		cnt = val_cnt;
1058 	} else {
1059 		if (pdp->pd_defval.vd_name == NULL)
1060 			return (DLADM_STATUS_NOTSUP);
1061 
1062 		if ((vdp = malloc(sizeof (val_desc_t))) == NULL)
1063 			return (DLADM_STATUS_NOMEM);
1064 
1065 		(void) memcpy(vdp, &pdp->pd_defval, sizeof (val_desc_t));
1066 		cnt = 1;
1067 	}
1068 
1069 	status = pdp->pd_set(link, vdp, cnt);
1070 
1071 	free(vdp);
1072 	return (status);
1073 }
1074 
1075 static dladm_status_t
1076 i_dladm_set_prop_temp(const char *link, const char *prop_name, char **prop_val,
1077     uint_t val_cnt, uint_t flags, char **errprop)
1078 {
1079 	int 		i;
1080 	dladm_status_t	status = DLADM_STATUS_OK;
1081 	boolean_t	found = B_FALSE;
1082 
1083 	for (i = 0; i < MAX_PROPS; i++) {
1084 		prop_desc_t	*pdp = &prop_table[i];
1085 		dladm_status_t	s;
1086 
1087 		if (prop_name != NULL &&
1088 		    (strcasecmp(prop_name, pdp->pd_name) != 0))
1089 			continue;
1090 
1091 		found = B_TRUE;
1092 		s = i_dladm_set_one_prop_temp(link, pdp, prop_val, val_cnt,
1093 		    flags);
1094 
1095 		if (prop_name != NULL) {
1096 			status = s;
1097 			break;
1098 		} else {
1099 			if (s != DLADM_STATUS_OK &&
1100 			    s != DLADM_STATUS_NOTSUP) {
1101 				if (errprop != NULL)
1102 					*errprop = pdp->pd_name;
1103 				status = s;
1104 				break;
1105 			}
1106 		}
1107 	}
1108 
1109 	if (!found)
1110 		status = DLADM_STATUS_NOTFOUND;
1111 
1112 	return (status);
1113 }
1114 
1115 static boolean_t
1116 i_dladm_is_prop_temponly(const char *prop_name, char **errprop)
1117 {
1118 	int 		i;
1119 
1120 	for (i = 0; i < MAX_PROPS; i++) {
1121 		prop_desc_t	*pdp = &prop_table[i];
1122 
1123 		if (prop_name != NULL &&
1124 		    (strcasecmp(prop_name, pdp->pd_name) != 0))
1125 			continue;
1126 
1127 		if (errprop != NULL)
1128 			*errprop = pdp->pd_name;
1129 
1130 		if (pdp->pd_temponly)
1131 			return (B_TRUE);
1132 	}
1133 
1134 	return (B_FALSE);
1135 }
1136