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