xref: /titanic_50/usr/src/lib/libdladm/common/linkprop.c (revision 2f79bea12c9814c8829dad82312f3c944423bcce)
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 2008 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 <stddef.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <sys/dld.h>
36 #include <sys/zone.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <libdevinfo.h>
40 #include <zone.h>
41 #include <libdllink.h>
42 #include <libdladm_impl.h>
43 #include <libdlwlan_impl.h>
44 #include <libdlwlan.h>
45 #include <libdlvlan.h>
46 #include <dlfcn.h>
47 #include <link.h>
48 #include <inet/wifi_ioctl.h>
49 
50 /*
51  * The linkprop get() callback.
52  * - propstrp:	a property string array to keep the returned property.
53  *		Caller allocated.
54  * - cntp:	number of returned properties.
55  *		Caller also uses it to indicate how many it expects.
56  */
57 typedef dladm_status_t	pd_getf_t(datalink_id_t, char **propstp, uint_t *cntp);
58 
59 /*
60  * The linkprop set() callback.
61  * - propval:	a val_desc_t array which keeps the property values to be set.
62  * - cnt:	number of properties to be set.
63  */
64 typedef dladm_status_t	pd_setf_t(datalink_id_t, val_desc_t *propval,
65 			    uint_t cnt);
66 
67 #define	PD_TEMPONLY	0x1
68 
69 /*
70  * The linkprop check() callback.
71  * - propstrp:	property string array which keeps the property to be checked.
72  * - cnt:	number of properties.
73  * - propval:	return value; the property values of the given property strings.
74  * - dofree:	indicates whether the caller needs to free propvalp->vd_val.
75  */
76 typedef dladm_status_t	pd_checkf_t(datalink_id_t, char **propstrp,
77 			    uint_t cnt, val_desc_t *propval, boolean_t *dofree);
78 
79 static pd_getf_t	do_get_zone, do_get_autopush, do_get_rate_mod,
80 			do_get_rate_prop, do_get_channel_prop,
81 			do_get_powermode_prop, do_get_radio_prop;
82 static pd_setf_t	do_set_zone, do_set_autopush, do_set_rate_prop,
83 			do_set_powermode_prop, do_set_radio_prop;
84 static pd_checkf_t	do_check_zone, do_check_autopush, do_check_rate;
85 
86 typedef struct prop_desc {
87 	/*
88 	 * link property name
89 	 */
90 	char			*pd_name;
91 
92 	/*
93 	 * default property value, can be set to { "", NULL }
94 	 */
95 	val_desc_t		pd_defval;
96 
97 	/*
98 	 * list of optional property values, can be NULL.
99 	 *
100 	 * This is set to non-NULL if there is a list of possible property
101 	 * values.  pd_optval would point to the array of possible values.
102 	 */
103 	val_desc_t		*pd_optval;
104 
105 	/*
106 	 * count of the above optional property values. 0 if pd_optval is NULL.
107 	 */
108 	uint_t			pd_noptval;
109 
110 	/*
111 	 * callback to set link property;
112 	 * set to NULL if this property is read-only
113 	 */
114 	pd_setf_t		*pd_set;
115 
116 	/*
117 	 * callback to get modifiable link property
118 	 */
119 	pd_getf_t		*pd_getmod;
120 
121 	/*
122 	 * callback to get current link property
123 	 */
124 	pd_getf_t		*pd_get;
125 
126 	/*
127 	 * callback to validate link property value, set to NULL if pd_optval
128 	 * is not NULL. In that case, validate the value by comparing it with
129 	 * the pd_optval. Return a val_desc_t array pointer if the value is
130 	 * valid.
131 	 */
132 	pd_checkf_t		*pd_check;
133 
134 	/*
135 	 * currently only PD_TEMPONLY is valid, which indicates the property
136 	 * is temporary only.
137 	 */
138 	uint_t			pd_flags;
139 
140 	/*
141 	 * indicate link classes this property applies to.
142 	 */
143 	datalink_class_t	pd_class;
144 
145 	/*
146 	 * indicate link media type this property applies to.
147 	 */
148 	datalink_media_t	pd_dmedia;
149 } prop_desc_t;
150 
151 static val_desc_t	dladm_wlan_radio_vals[] = {
152 	{ "on",		DLADM_WLAN_RADIO_ON	},
153 	{ "off",	DLADM_WLAN_RADIO_OFF	}
154 };
155 
156 static val_desc_t	dladm_wlan_powermode_vals[] = {
157 	{ "off",	DLADM_WLAN_PM_OFF	},
158 	{ "fast",	DLADM_WLAN_PM_FAST	},
159 	{ "max",	DLADM_WLAN_PM_MAX	}
160 };
161 
162 static prop_desc_t	prop_table[] = {
163 
164 	{ "channel",	{ NULL, 0 }, NULL, 0, NULL, NULL,
165 	    do_get_channel_prop, NULL, 0,
166 	    DATALINK_CLASS_PHYS, DL_WIFI},
167 
168 	{ "powermode",	{ "off", DLADM_WLAN_PM_OFF },
169 	    dladm_wlan_powermode_vals, VALCNT(dladm_wlan_powermode_vals),
170 	    do_set_powermode_prop, NULL,
171 	    do_get_powermode_prop, NULL, 0,
172 	    DATALINK_CLASS_PHYS, DL_WIFI},
173 
174 	{ "radio",	{ "on", DLADM_WLAN_RADIO_ON },
175 	    dladm_wlan_radio_vals, VALCNT(dladm_wlan_radio_vals),
176 	    do_set_radio_prop, NULL,
177 	    do_get_radio_prop, NULL, 0,
178 	    DATALINK_CLASS_PHYS, DL_WIFI},
179 
180 	{ "speed",	{ "", 0 }, NULL, 0,
181 	    do_set_rate_prop, do_get_rate_mod,
182 	    do_get_rate_prop, do_check_rate, 0,
183 	    DATALINK_CLASS_PHYS, DL_WIFI},
184 
185 	{ "autopush",	{ "", NULL }, NULL, 0,
186 	    do_set_autopush, NULL,
187 	    do_get_autopush, do_check_autopush, 0,
188 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE},
189 
190 	{ "zone",	{ "", NULL }, NULL, 0,
191 	    do_set_zone, NULL,
192 	    do_get_zone, do_check_zone, PD_TEMPONLY,
193 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE}
194 };
195 
196 #define	DLADM_MAX_PROPS	(sizeof (prop_table) / sizeof (prop_desc_t))
197 
198 static dladm_status_t	i_dladm_set_linkprop_db(datalink_id_t, const char *,
199 			    char **, uint_t);
200 static dladm_status_t	i_dladm_get_linkprop_db(datalink_id_t, const char *,
201 			    char **, uint_t *);
202 static dladm_status_t	i_dladm_set_single_prop(datalink_id_t, datalink_class_t,
203 			    uint32_t, prop_desc_t *, char **, uint_t, uint_t);
204 static dladm_status_t	i_dladm_set_linkprop(datalink_id_t, const char *,
205 			    char **, uint_t, uint_t);
206 
207 /*
208  * Unfortunately, MAX_SCAN_SUPPORT_RATES is too small to allow all
209  * rates to be retrieved. However, we cannot increase it at this
210  * time because it will break binary compatibility with unbundled
211  * WiFi drivers and utilities. So for now we define an additional
212  * constant, MAX_SUPPORT_RATES, to allow all rates to be retrieved.
213  */
214 #define	MAX_SUPPORT_RATES	64
215 
216 #define	AP_ANCHOR	"[anchor]"
217 #define	AP_DELIMITER	'.'
218 
219 static dladm_status_t
220 do_check_prop(prop_desc_t *pdp, char **prop_val, uint_t val_cnt,
221     val_desc_t *vdp)
222 {
223 	int		i, j;
224 	dladm_status_t	status = DLADM_STATUS_OK;
225 
226 	for (j = 0; j < val_cnt; j++) {
227 		for (i = 0; i < pdp->pd_noptval; i++) {
228 			if (strcasecmp(*prop_val,
229 			    pdp->pd_optval[i].vd_name) == 0) {
230 				break;
231 			}
232 		}
233 		if (i == pdp->pd_noptval) {
234 			status = DLADM_STATUS_BADVAL;
235 			goto done;
236 		}
237 		(void) memcpy(vdp + j, &pdp->pd_optval[i], sizeof (val_desc_t));
238 	}
239 
240 done:
241 	return (status);
242 }
243 
244 static dladm_status_t
245 i_dladm_set_single_prop(datalink_id_t linkid, datalink_class_t class,
246     uint32_t media, prop_desc_t *pdp, char **prop_val, uint_t val_cnt,
247     uint_t flags)
248 {
249 	dladm_status_t	status = DLADM_STATUS_OK;
250 	val_desc_t	*vdp = NULL;
251 	boolean_t	needfree = B_FALSE;
252 	uint_t		cnt, i;
253 
254 	if (!(pdp->pd_class & class))
255 		return (DLADM_STATUS_BADARG);
256 
257 	if (!DATALINK_MEDIA_ACCEPTED(pdp->pd_dmedia, media))
258 		return (DLADM_STATUS_BADARG);
259 
260 	if ((flags & DLADM_OPT_PERSIST) && (pdp->pd_flags & PD_TEMPONLY))
261 		return (DLADM_STATUS_TEMPONLY);
262 
263 	if (!(flags & DLADM_OPT_ACTIVE))
264 		return (DLADM_STATUS_OK);
265 
266 	if (pdp->pd_set == NULL)
267 		return (DLADM_STATUS_PROPRDONLY);
268 
269 	if (prop_val != NULL) {
270 		vdp = malloc(sizeof (val_desc_t) * val_cnt);
271 		if (vdp == NULL)
272 			return (DLADM_STATUS_NOMEM);
273 
274 		if (pdp->pd_check != NULL) {
275 			status = pdp->pd_check(linkid, prop_val, val_cnt, vdp,
276 			    &needfree);
277 		} else if (pdp->pd_optval != NULL) {
278 			status = do_check_prop(pdp, prop_val, val_cnt, vdp);
279 		} else {
280 			status = DLADM_STATUS_BADARG;
281 		}
282 
283 		if (status != DLADM_STATUS_OK)
284 			goto done;
285 
286 		cnt = val_cnt;
287 	} else {
288 		if (pdp->pd_defval.vd_name == NULL)
289 			return (DLADM_STATUS_NOTSUP);
290 
291 		if ((vdp = malloc(sizeof (val_desc_t))) == NULL)
292 			return (DLADM_STATUS_NOMEM);
293 
294 		(void) memcpy(vdp, &pdp->pd_defval, sizeof (val_desc_t));
295 		cnt = 1;
296 	}
297 	status = pdp->pd_set(linkid, vdp, cnt);
298 	if (needfree) {
299 		for (i = 0; i < cnt; i++)
300 			free((void *)(((val_desc_t *)vdp + i)->vd_val));
301 	}
302 done:
303 	free(vdp);
304 	return (status);
305 }
306 
307 static dladm_status_t
308 i_dladm_set_linkprop(datalink_id_t linkid, const char *prop_name,
309     char **prop_val, uint_t val_cnt, uint_t flags)
310 {
311 	int			i;
312 	boolean_t		found = B_FALSE;
313 	datalink_class_t	class;
314 	uint32_t		media;
315 	dladm_status_t		status = DLADM_STATUS_OK;
316 
317 	status = dladm_datalink_id2info(linkid, NULL, &class, &media, NULL, 0);
318 	if (status != DLADM_STATUS_OK)
319 		return (status);
320 
321 	for (i = 0; i < DLADM_MAX_PROPS; i++) {
322 		prop_desc_t	*pdp = &prop_table[i];
323 		dladm_status_t	s;
324 
325 		if (prop_name != NULL &&
326 		    (strcasecmp(prop_name, pdp->pd_name) != 0))
327 			continue;
328 
329 		found = B_TRUE;
330 		s = i_dladm_set_single_prop(linkid, class, media, pdp, prop_val,
331 		    val_cnt, flags);
332 
333 		if (prop_name != NULL) {
334 			status = s;
335 			break;
336 		} else {
337 			if (s != DLADM_STATUS_OK &&
338 			    s != DLADM_STATUS_NOTSUP)
339 				status = s;
340 		}
341 	}
342 	if (!found)
343 		status = DLADM_STATUS_NOTFOUND;
344 
345 	return (status);
346 }
347 
348 /*
349  * Set/reset link property for specific link
350  */
351 dladm_status_t
352 dladm_set_linkprop(datalink_id_t linkid, const char *prop_name, char **prop_val,
353     uint_t val_cnt, uint_t flags)
354 {
355 	dladm_status_t	status = DLADM_STATUS_OK;
356 
357 	if ((linkid == DATALINK_INVALID_LINKID) || (flags == 0) ||
358 	    (prop_val == NULL && val_cnt > 0) ||
359 	    (prop_val != NULL && val_cnt == 0) ||
360 	    (prop_name == NULL && prop_val != NULL)) {
361 		return (DLADM_STATUS_BADARG);
362 	}
363 
364 	status = i_dladm_set_linkprop(linkid, prop_name, prop_val,
365 	    val_cnt, flags);
366 	if (status != DLADM_STATUS_OK)
367 		return (status);
368 
369 	if (flags & DLADM_OPT_PERSIST) {
370 		status = i_dladm_set_linkprop_db(linkid, prop_name,
371 		    prop_val, val_cnt);
372 	}
373 	return (status);
374 }
375 
376 /*
377  * Walk link properties of the given specific link.
378  */
379 dladm_status_t
380 dladm_walk_linkprop(datalink_id_t linkid, void *arg,
381     int (*func)(datalink_id_t, const char *, void *))
382 {
383 	dladm_status_t		status;
384 	datalink_class_t	class;
385 	uint_t			media;
386 	int			i;
387 
388 	if (linkid == DATALINK_INVALID_LINKID || func == NULL)
389 		return (DLADM_STATUS_BADARG);
390 
391 	status = dladm_datalink_id2info(linkid, NULL, &class, &media, NULL, 0);
392 	if (status != DLADM_STATUS_OK)
393 		return (status);
394 
395 	for (i = 0; i < DLADM_MAX_PROPS; i++) {
396 		if (!(prop_table[i].pd_class & class))
397 			continue;
398 
399 		if (!DATALINK_MEDIA_ACCEPTED(prop_table[i].pd_dmedia, media))
400 			continue;
401 
402 		if (func(linkid, prop_table[i].pd_name, arg) ==
403 		    DLADM_WALK_TERMINATE) {
404 			break;
405 		}
406 	}
407 
408 	return (DLADM_STATUS_OK);
409 }
410 
411 /*
412  * Get linkprop of the given specific link.
413  */
414 dladm_status_t
415 dladm_get_linkprop(datalink_id_t linkid, dladm_prop_type_t type,
416     const char *prop_name, char **prop_val, uint_t *val_cntp)
417 {
418 	dladm_status_t		status = DLADM_STATUS_OK;
419 	datalink_class_t	class;
420 	uint_t			media;
421 	prop_desc_t		*pdp;
422 	uint_t			cnt;
423 	int			i;
424 
425 	if (linkid == DATALINK_INVALID_LINKID || prop_name == NULL ||
426 	    prop_val == NULL || val_cntp == NULL || *val_cntp == 0)
427 		return (DLADM_STATUS_BADARG);
428 
429 	for (i = 0; i < DLADM_MAX_PROPS; i++)
430 		if (strcasecmp(prop_name, prop_table[i].pd_name) == 0)
431 			break;
432 
433 	if (i == DLADM_MAX_PROPS)
434 		return (DLADM_STATUS_NOTFOUND);
435 
436 	pdp = &prop_table[i];
437 
438 	status = dladm_datalink_id2info(linkid, NULL, &class, &media, NULL, 0);
439 	if (status != DLADM_STATUS_OK)
440 		return (status);
441 
442 	if (!(pdp->pd_class & class))
443 		return (DLADM_STATUS_BADARG);
444 
445 	if (!DATALINK_MEDIA_ACCEPTED(pdp->pd_dmedia, media))
446 		return (DLADM_STATUS_BADARG);
447 
448 	switch (type) {
449 	case DLADM_PROP_VAL_CURRENT:
450 		status = pdp->pd_get(linkid, prop_val, val_cntp);
451 		break;
452 
453 	case DLADM_PROP_VAL_DEFAULT:
454 		if (pdp->pd_defval.vd_name == NULL) {
455 			status = DLADM_STATUS_NOTSUP;
456 			break;
457 		}
458 		(void) strcpy(*prop_val, pdp->pd_defval.vd_name);
459 		*val_cntp = 1;
460 		break;
461 
462 	case DLADM_PROP_VAL_MODIFIABLE:
463 		if (pdp->pd_getmod != NULL) {
464 			status = pdp->pd_getmod(linkid, prop_val, val_cntp);
465 			break;
466 		}
467 		cnt = pdp->pd_noptval;
468 		if (cnt == 0) {
469 			status = DLADM_STATUS_NOTSUP;
470 		} else if (cnt > *val_cntp) {
471 			status = DLADM_STATUS_TOOSMALL;
472 		} else {
473 			for (i = 0; i < cnt; i++) {
474 				(void) strcpy(prop_val[i],
475 				    pdp->pd_optval[i].vd_name);
476 			}
477 			*val_cntp = cnt;
478 		}
479 		break;
480 	case DLADM_PROP_VAL_PERSISTENT:
481 		if (pdp->pd_flags & PD_TEMPONLY)
482 			return (DLADM_STATUS_TEMPONLY);
483 		status = i_dladm_get_linkprop_db(linkid, prop_name,
484 		    prop_val, val_cntp);
485 		break;
486 	default:
487 		status = DLADM_STATUS_BADARG;
488 		break;
489 	}
490 
491 	return (status);
492 }
493 
494 /*ARGSUSED*/
495 static int
496 i_dladm_init_one_prop(datalink_id_t linkid, const char *prop_name, void *arg)
497 {
498 	char	*buf, **propvals;
499 	uint_t	i, valcnt = DLADM_MAX_PROP_VALCNT;
500 
501 	if ((buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) *
502 	    DLADM_MAX_PROP_VALCNT)) == NULL) {
503 		return (DLADM_WALK_CONTINUE);
504 	}
505 
506 	propvals = (char **)(void *)buf;
507 	for (i = 0; i < valcnt; i++) {
508 		propvals[i] = buf +
509 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
510 		    i * DLADM_PROP_VAL_MAX;
511 	}
512 
513 	if (dladm_get_linkprop(linkid, DLADM_PROP_VAL_PERSISTENT, prop_name,
514 	    propvals, &valcnt) != DLADM_STATUS_OK) {
515 		goto done;
516 	}
517 
518 	(void) dladm_set_linkprop(linkid, prop_name, propvals, valcnt,
519 	    DLADM_OPT_ACTIVE);
520 
521 done:
522 	if (buf != NULL)
523 		free(buf);
524 
525 	return (DLADM_WALK_CONTINUE);
526 }
527 
528 /*ARGSUSED*/
529 static int
530 i_dladm_init_linkprop(datalink_id_t linkid, void *arg)
531 {
532 	(void) dladm_init_linkprop(linkid);
533 	return (DLADM_WALK_CONTINUE);
534 }
535 
536 dladm_status_t
537 dladm_init_linkprop(datalink_id_t linkid)
538 {
539 	if (linkid == DATALINK_ALL_LINKID) {
540 		(void) dladm_walk_datalink_id(i_dladm_init_linkprop, NULL,
541 		    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
542 		    DLADM_OPT_PERSIST);
543 	} else {
544 		(void) dladm_walk_linkprop(linkid, NULL, i_dladm_init_one_prop);
545 	}
546 	return (DLADM_STATUS_OK);
547 }
548 
549 static dladm_status_t
550 do_get_zone(datalink_id_t linkid, char **prop_val, uint_t *val_cnt)
551 {
552 	char		zone_name[ZONENAME_MAX];
553 	zoneid_t	zid;
554 	dladm_status_t	status;
555 
556 	status = dladm_getzid(linkid, &zid);
557 	if (status != DLADM_STATUS_OK)
558 		return (status);
559 
560 	*val_cnt = 1;
561 	if (zid != GLOBAL_ZONEID) {
562 		if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0)
563 			return (dladm_errno2status(errno));
564 
565 		(void) strncpy(*prop_val, zone_name, DLADM_PROP_VAL_MAX);
566 	} else {
567 		*prop_val[0] = '\0';
568 	}
569 
570 	return (DLADM_STATUS_OK);
571 }
572 
573 typedef int (*zone_get_devroot_t)(char *, char *, size_t);
574 
575 static int
576 i_dladm_get_zone_dev(char *zone_name, char *dev, size_t devlen)
577 {
578 	char			root[MAXPATHLEN];
579 	zone_get_devroot_t	real_zone_get_devroot;
580 	void			*dlhandle;
581 	void			*sym;
582 	int			ret;
583 
584 	if ((dlhandle = dlopen("libzonecfg.so.1", RTLD_LAZY)) == NULL)
585 		return (-1);
586 
587 	if ((sym = dlsym(dlhandle, "zone_get_devroot")) == NULL) {
588 		(void) dlclose(dlhandle);
589 		return (-1);
590 	}
591 
592 	real_zone_get_devroot = (zone_get_devroot_t)sym;
593 
594 	if ((ret = real_zone_get_devroot(zone_name, root, sizeof (root))) == 0)
595 		(void) snprintf(dev, devlen, "%s%s", root, "/dev");
596 	(void) dlclose(dlhandle);
597 	return (ret);
598 }
599 
600 static dladm_status_t
601 i_dladm_update_deventry(zoneid_t zid, datalink_id_t linkid, boolean_t add)
602 {
603 	char		path[MAXPATHLEN];
604 	char		name[MAXLINKNAMELEN];
605 	di_prof_t	prof = NULL;
606 	char		zone_name[ZONENAME_MAX];
607 	dladm_status_t	status;
608 	int		ret;
609 
610 	if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0)
611 		return (dladm_errno2status(errno));
612 	if (i_dladm_get_zone_dev(zone_name, path, sizeof (path)) != 0)
613 		return (dladm_errno2status(errno));
614 	if (di_prof_init(path, &prof) != 0)
615 		return (dladm_errno2status(errno));
616 
617 	status = dladm_linkid2legacyname(linkid, name, MAXLINKNAMELEN);
618 	if (status != DLADM_STATUS_OK)
619 		goto cleanup;
620 
621 	if (add)
622 		ret = di_prof_add_dev(prof, name);
623 	else
624 		ret = di_prof_add_exclude(prof, name);
625 
626 	if (ret != 0) {
627 		status = dladm_errno2status(errno);
628 		goto cleanup;
629 	}
630 
631 	if (di_prof_commit(prof) != 0)
632 		status = dladm_errno2status(errno);
633 cleanup:
634 	if (prof)
635 		di_prof_fini(prof);
636 
637 	return (status);
638 }
639 
640 static dladm_status_t
641 do_set_zone(datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt)
642 {
643 	dladm_status_t	status;
644 	zoneid_t	zid_old, zid_new;
645 	char		link[MAXLINKNAMELEN];
646 
647 	if (val_cnt != 1)
648 		return (DLADM_STATUS_BADVALCNT);
649 
650 	status = dladm_getzid(linkid, &zid_old);
651 	if (status != DLADM_STATUS_OK)
652 		return (status);
653 
654 	/* Do nothing if setting to current value */
655 	zid_new = vdp->vd_val;
656 	if (zid_new == zid_old)
657 		return (DLADM_STATUS_OK);
658 
659 	if ((status = dladm_datalink_id2info(linkid, NULL, NULL, NULL,
660 	    link, MAXLINKNAMELEN)) != DLADM_STATUS_OK) {
661 		return (status);
662 	}
663 
664 	if (zid_new != GLOBAL_ZONEID) {
665 		/*
666 		 * If the new zoneid is the global zone, we could destroy
667 		 * the link (in the case of an implicitly-created VLAN) as a
668 		 * result of the dladm_setzid() operation. In that case,
669 		 * we defer the operation to the end of this function to avoid
670 		 * recreating the VLAN and getting a different linkid during
671 		 * the rollback if other operation fails.
672 		 *
673 		 * Otherwise, dladm_setzid() will hold a reference to the
674 		 * link and prevent a link renaming, so we need to do it
675 		 * before other operations.
676 		 */
677 		status = dladm_setzid(link, zid_new);
678 		if (status != DLADM_STATUS_OK)
679 			return (status);
680 	}
681 
682 	if (zid_old != GLOBAL_ZONEID) {
683 		if (zone_remove_datalink(zid_old, link) != 0 &&
684 		    errno != ENXIO) {
685 			status = dladm_errno2status(errno);
686 			goto rollback1;
687 		}
688 
689 		/*
690 		 * It is okay to fail to update the /dev entry (some
691 		 * vanity-named links do not have a /dev entry).
692 		 */
693 		(void) i_dladm_update_deventry(zid_old, linkid, B_FALSE);
694 	}
695 
696 	if (zid_new != GLOBAL_ZONEID) {
697 		if (zone_add_datalink(zid_new, link) != 0) {
698 			status = dladm_errno2status(errno);
699 			goto rollback2;
700 		}
701 
702 		(void) i_dladm_update_deventry(zid_new, linkid, B_TRUE);
703 	} else {
704 		status = dladm_setzid(link, zid_new);
705 		if (status != DLADM_STATUS_OK)
706 			goto rollback2;
707 	}
708 
709 	return (DLADM_STATUS_OK);
710 
711 rollback2:
712 	if (zid_old != GLOBAL_ZONEID)
713 		(void) i_dladm_update_deventry(zid_old, linkid, B_TRUE);
714 	if (zid_old != GLOBAL_ZONEID)
715 		(void) zone_add_datalink(zid_old, link);
716 rollback1:
717 	if (zid_new != GLOBAL_ZONEID)
718 		(void) dladm_setzid(link, zid_old);
719 	return (status);
720 }
721 
722 /* ARGSUSED */
723 static dladm_status_t
724 do_check_zone(datalink_id_t linkid, char **prop_val, uint_t val_cnt,
725     val_desc_t *vdp, boolean_t *needfreep)
726 {
727 	zoneid_t	zid;
728 
729 	if (val_cnt != 1)
730 		return (DLADM_STATUS_BADVALCNT);
731 
732 	if ((zid = getzoneidbyname(*prop_val)) == -1)
733 		return (DLADM_STATUS_BADVAL);
734 
735 	if (zid != GLOBAL_ZONEID) {
736 		ushort_t	flags;
737 
738 		if (zone_getattr(zid, ZONE_ATTR_FLAGS, &flags,
739 		    sizeof (flags)) < 0) {
740 			return (dladm_errno2status(errno));
741 		}
742 
743 		if (!(flags & ZF_NET_EXCL)) {
744 			return (DLADM_STATUS_BADVAL);
745 		}
746 	}
747 
748 	vdp->vd_val = zid;
749 	*needfreep = B_FALSE;
750 	return (DLADM_STATUS_OK);
751 }
752 
753 static dladm_status_t
754 do_get_autopush(datalink_id_t linkid, char **prop_val, uint_t *val_cnt)
755 {
756 	dld_ioc_ap_t	dia;
757 	int		fd, i, len;
758 
759 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
760 		return (dladm_errno2status(errno));
761 
762 	*val_cnt = 1;
763 	dia.dia_linkid = linkid;
764 	if (i_dladm_ioctl(fd, DLDIOC_GETAUTOPUSH, &dia, sizeof (dia)) < 0) {
765 		(*prop_val)[0] = '\0';
766 		goto done;
767 	}
768 
769 	for (i = 0, len = 0; i < dia.dia_npush; i++) {
770 		if (i != 0) {
771 			(void) snprintf(*prop_val + len,
772 			    DLADM_PROP_VAL_MAX - len, "%c", AP_DELIMITER);
773 			len += 1;
774 		}
775 		(void) snprintf(*prop_val + len, DLADM_PROP_VAL_MAX - len,
776 		    "%s", dia.dia_aplist[i]);
777 		len += strlen(dia.dia_aplist[i]);
778 		if (dia.dia_anchor - 1 == i) {
779 			(void) snprintf(*prop_val + len,
780 			    DLADM_PROP_VAL_MAX - len, "%c%s", AP_DELIMITER,
781 			    AP_ANCHOR);
782 			len += (strlen(AP_ANCHOR) + 1);
783 		}
784 	}
785 
786 done:
787 	(void) close(fd);
788 	return (DLADM_STATUS_OK);
789 }
790 
791 static dladm_status_t
792 do_set_autopush(datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt)
793 {
794 	dld_ioc_ap_t		dia;
795 	struct dlautopush	*dlap = (struct dlautopush *)vdp->vd_val;
796 	dladm_status_t		status = DLADM_STATUS_OK;
797 	int			fd, i;
798 	int			ic_cmd;
799 
800 	if (val_cnt != 1)
801 		return (DLADM_STATUS_BADVALCNT);
802 
803 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
804 		return (dladm_errno2status(errno));
805 
806 	dia.dia_linkid = linkid;
807 	if (dlap != NULL) {
808 		dia.dia_anchor = dlap->dap_anchor;
809 		dia.dia_npush = dlap->dap_npush;
810 		for (i = 0; i < dia.dia_npush; i++) {
811 			(void) strlcpy(dia.dia_aplist[i], dlap->dap_aplist[i],
812 			    FMNAMESZ+1);
813 		}
814 		ic_cmd = DLDIOC_SETAUTOPUSH;
815 	} else {
816 		ic_cmd = DLDIOC_CLRAUTOPUSH;
817 	}
818 
819 	if (i_dladm_ioctl(fd, ic_cmd, &dia, sizeof (dia)) < 0)
820 		status = dladm_errno2status(errno);
821 
822 	(void) close(fd);
823 	return (status);
824 }
825 
826 /*
827  * Add the specified module to the dlautopush structure; returns a
828  * DLADM_STATUS_* code.
829  */
830 dladm_status_t
831 i_dladm_add_ap_module(const char *module, struct dlautopush *dlap)
832 {
833 	if ((strlen(module) == 0) || (strlen(module) > FMNAMESZ))
834 		return (DLADM_STATUS_BADVAL);
835 
836 	if (strncasecmp(module, AP_ANCHOR, strlen(AP_ANCHOR)) == 0) {
837 		/*
838 		 * We don't allow multiple anchors, and the anchor must
839 		 * be after at least one module.
840 		 */
841 		if (dlap->dap_anchor != 0)
842 			return (DLADM_STATUS_BADVAL);
843 		if (dlap->dap_npush == 0)
844 			return (DLADM_STATUS_BADVAL);
845 
846 		dlap->dap_anchor = dlap->dap_npush;
847 		return (DLADM_STATUS_OK);
848 	}
849 	if (dlap->dap_npush > MAXAPUSH)
850 		return (DLADM_STATUS_BADVALCNT);
851 
852 	(void) strlcpy(dlap->dap_aplist[dlap->dap_npush++], module,
853 	    FMNAMESZ + 1);
854 
855 	return (DLADM_STATUS_OK);
856 }
857 
858 /*
859  * Currently, both '.' and ' '(space) can be used as the delimiters between
860  * autopush modules. The former is used in dladm set-linkprop, and the
861  * latter is used in the autopush(1M) file.
862  */
863 /* ARGSUSED */
864 static dladm_status_t
865 do_check_autopush(datalink_id_t linkid, char **prop_val, uint_t val_cnt,
866     val_desc_t *vdp, boolean_t *needfreep)
867 {
868 	char			*module;
869 	struct dlautopush	*dlap;
870 	dladm_status_t		status;
871 	char			val[DLADM_PROP_VAL_MAX];
872 	char			delimiters[4];
873 
874 	if (val_cnt != 1)
875 		return (DLADM_STATUS_BADVALCNT);
876 
877 	dlap = malloc(sizeof (struct dlautopush));
878 	if (dlap == NULL)
879 		return (DLADM_STATUS_NOMEM);
880 
881 	(void) memset(dlap, 0, sizeof (struct dlautopush));
882 	(void) snprintf(delimiters, 4, " %c\n", AP_DELIMITER);
883 	bcopy(*prop_val, val, DLADM_PROP_VAL_MAX);
884 	module = strtok(val, delimiters);
885 	while (module != NULL) {
886 		status = i_dladm_add_ap_module(module, dlap);
887 		if (status != DLADM_STATUS_OK)
888 			return (status);
889 		module = strtok(NULL, delimiters);
890 	}
891 
892 	vdp->vd_val = (uintptr_t)dlap;
893 	*needfreep = B_TRUE;
894 	return (DLADM_STATUS_OK);
895 }
896 
897 static dladm_status_t
898 do_get_rate_common(datalink_id_t linkid, char **prop_val, uint_t *val_cnt,
899     uint_t id)
900 {
901 	wl_rates_t	*wrp;
902 	uint_t		i;
903 	wldp_t		*gbuf = NULL;
904 	dladm_status_t	status = DLADM_STATUS_OK;
905 
906 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) {
907 		status = DLADM_STATUS_NOMEM;
908 		goto done;
909 	}
910 
911 	status = i_dladm_wlan_get_ioctl(linkid, gbuf, id);
912 	if (status != DLADM_STATUS_OK)
913 		goto done;
914 
915 	wrp = (wl_rates_t *)gbuf->wldp_buf;
916 	if (wrp->wl_rates_num > *val_cnt) {
917 		status = DLADM_STATUS_TOOSMALL;
918 		goto done;
919 	}
920 
921 	if (wrp->wl_rates_rates[0] == 0) {
922 		prop_val[0][0] = '\0';
923 		*val_cnt = 1;
924 		goto done;
925 	}
926 
927 	for (i = 0; i < wrp->wl_rates_num; i++) {
928 		(void) snprintf(prop_val[i], DLADM_STRSIZE, "%.*f",
929 		    wrp->wl_rates_rates[i] % 2,
930 		    (float)wrp->wl_rates_rates[i] / 2);
931 	}
932 	*val_cnt = wrp->wl_rates_num;
933 
934 done:
935 	free(gbuf);
936 	return (status);
937 }
938 
939 static dladm_status_t
940 do_get_rate_prop(datalink_id_t linkid, char **prop_val, uint_t *val_cnt)
941 {
942 	return (do_get_rate_common(linkid, prop_val, val_cnt,
943 	    WL_DESIRED_RATES));
944 }
945 
946 static dladm_status_t
947 do_get_rate_mod(datalink_id_t linkid, char **prop_val, uint_t *val_cnt)
948 {
949 	return (do_get_rate_common(linkid, prop_val, val_cnt,
950 	    WL_SUPPORTED_RATES));
951 }
952 
953 static dladm_status_t
954 do_set_rate(datalink_id_t linkid, dladm_wlan_rates_t *rates)
955 {
956 	int		i;
957 	uint_t		len;
958 	wldp_t		*gbuf;
959 	wl_rates_t	*wrp;
960 	dladm_status_t	status = DLADM_STATUS_OK;
961 
962 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
963 		return (DLADM_STATUS_NOMEM);
964 
965 	(void) memset(gbuf, 0, MAX_BUF_LEN);
966 
967 	wrp = (wl_rates_t *)gbuf->wldp_buf;
968 	for (i = 0; i < rates->wr_cnt; i++)
969 		wrp->wl_rates_rates[i] = rates->wr_rates[i];
970 	wrp->wl_rates_num = rates->wr_cnt;
971 
972 	len = offsetof(wl_rates_t, wl_rates_rates) +
973 	    (rates->wr_cnt * sizeof (char)) + WIFI_BUF_OFFSET;
974 	status = i_dladm_wlan_ioctl(linkid, gbuf, WL_DESIRED_RATES, len,
975 	    WLAN_SET_PARAM, len);
976 
977 	free(gbuf);
978 	return (status);
979 }
980 
981 static dladm_status_t
982 do_set_rate_prop(datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt)
983 {
984 	dladm_wlan_rates_t	rates;
985 	dladm_status_t		status;
986 
987 	if (val_cnt != 1)
988 		return (DLADM_STATUS_BADVALCNT);
989 
990 	rates.wr_cnt = 1;
991 	rates.wr_rates[0] = vdp[0].vd_val;
992 
993 	status = do_set_rate(linkid, &rates);
994 
995 done:
996 	return (status);
997 }
998 
999 /* ARGSUSED */
1000 static dladm_status_t
1001 do_check_rate(datalink_id_t linkid, char **prop_val, uint_t val_cnt,
1002     val_desc_t *vdp, boolean_t *needfreep)
1003 {
1004 	int		i;
1005 	uint_t		modval_cnt = MAX_SUPPORT_RATES;
1006 	char		*buf, **modval;
1007 	dladm_status_t	status;
1008 
1009 	if (val_cnt != 1)
1010 		return (DLADM_STATUS_BADVALCNT);
1011 
1012 	buf = malloc((sizeof (char *) + DLADM_STRSIZE) *
1013 	    MAX_SUPPORT_RATES);
1014 	if (buf == NULL) {
1015 		status = DLADM_STATUS_NOMEM;
1016 		goto done;
1017 	}
1018 
1019 	modval = (char **)(void *)buf;
1020 	for (i = 0; i < MAX_SUPPORT_RATES; i++) {
1021 		modval[i] = buf + sizeof (char *) * MAX_SUPPORT_RATES +
1022 		    i * DLADM_STRSIZE;
1023 	}
1024 
1025 	status = do_get_rate_mod(linkid, modval, &modval_cnt);
1026 	if (status != DLADM_STATUS_OK)
1027 		goto done;
1028 
1029 	for (i = 0; i < modval_cnt; i++) {
1030 		if (strcasecmp(*prop_val, modval[i]) == 0) {
1031 			vdp->vd_val = (uint_t)(atof(*prop_val) * 2);
1032 			status = DLADM_STATUS_OK;
1033 
1034 			/*
1035 			 * Does not need the caller to free the vdp->vd_val
1036 			 */
1037 			*needfreep = B_FALSE;
1038 			break;
1039 		}
1040 	}
1041 	if (i == modval_cnt)
1042 		status = DLADM_STATUS_BADVAL;
1043 done:
1044 	free(buf);
1045 	return (status);
1046 }
1047 
1048 static dladm_status_t
1049 do_get_phyconf(datalink_id_t linkid, wldp_t *gbuf)
1050 {
1051 	return (i_dladm_wlan_get_ioctl(linkid, gbuf, WL_PHY_CONFIG));
1052 }
1053 
1054 static dladm_status_t
1055 do_get_channel_prop(datalink_id_t linkid, char **prop_val, uint_t *val_cnt)
1056 {
1057 	uint32_t	channel;
1058 	wldp_t		*gbuf;
1059 	dladm_status_t	status = DLADM_STATUS_OK;
1060 
1061 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
1062 		return (DLADM_STATUS_NOMEM);
1063 
1064 	if ((status = do_get_phyconf(linkid, gbuf)) != DLADM_STATUS_OK)
1065 		goto done;
1066 
1067 	if (!i_dladm_wlan_convert_chan((wl_phy_conf_t *)gbuf->wldp_buf,
1068 	    &channel)) {
1069 		status = DLADM_STATUS_NOTFOUND;
1070 		goto done;
1071 	}
1072 
1073 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%u", channel);
1074 	*val_cnt = 1;
1075 
1076 done:
1077 	free(gbuf);
1078 	return (status);
1079 }
1080 
1081 static dladm_status_t
1082 do_get_powermode(datalink_id_t linkid, wldp_t *gbuf)
1083 {
1084 	return (i_dladm_wlan_get_ioctl(linkid, gbuf, WL_POWER_MODE));
1085 }
1086 
1087 static dladm_status_t
1088 do_get_powermode_prop(datalink_id_t linkid, char **prop_val, uint_t *val_cnt)
1089 {
1090 	wl_ps_mode_t	*mode;
1091 	const char	*s;
1092 	wldp_t		*gbuf;
1093 	dladm_status_t	status = DLADM_STATUS_OK;
1094 
1095 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
1096 		return (DLADM_STATUS_NOMEM);
1097 
1098 	if ((status = do_get_powermode(linkid, gbuf)) != DLADM_STATUS_OK)
1099 		goto done;
1100 
1101 	mode = (wl_ps_mode_t *)(gbuf->wldp_buf);
1102 	switch (mode->wl_ps_mode) {
1103 	case WL_PM_AM:
1104 		s = "off";
1105 		break;
1106 	case WL_PM_MPS:
1107 		s = "max";
1108 		break;
1109 	case WL_PM_FAST:
1110 		s = "fast";
1111 		break;
1112 	default:
1113 		status = DLADM_STATUS_NOTFOUND;
1114 		goto done;
1115 	}
1116 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
1117 	*val_cnt = 1;
1118 
1119 done:
1120 	free(gbuf);
1121 	return (status);
1122 }
1123 
1124 static dladm_status_t
1125 do_set_powermode(datalink_id_t linkid, dladm_wlan_powermode_t *pm)
1126 {
1127 	wl_ps_mode_t    ps_mode;
1128 
1129 	(void) memset(&ps_mode, 0xff, sizeof (ps_mode));
1130 
1131 	switch (*pm) {
1132 	case DLADM_WLAN_PM_OFF:
1133 		ps_mode.wl_ps_mode = WL_PM_AM;
1134 		break;
1135 	case DLADM_WLAN_PM_MAX:
1136 		ps_mode.wl_ps_mode = WL_PM_MPS;
1137 		break;
1138 	case DLADM_WLAN_PM_FAST:
1139 		ps_mode.wl_ps_mode = WL_PM_FAST;
1140 		break;
1141 	default:
1142 		return (DLADM_STATUS_NOTSUP);
1143 	}
1144 	return (i_dladm_wlan_set_ioctl(linkid, WL_POWER_MODE, &ps_mode,
1145 	    sizeof (ps_mode)));
1146 }
1147 
1148 /* ARGSUSED */
1149 static dladm_status_t
1150 do_set_powermode_prop(datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt)
1151 {
1152 	dladm_wlan_powermode_t powermode = (dladm_wlan_powermode_t)vdp->vd_val;
1153 	dladm_status_t status;
1154 
1155 	if (val_cnt != 1)
1156 		return (DLADM_STATUS_BADVALCNT);
1157 
1158 	status = do_set_powermode(linkid, &powermode);
1159 
1160 	return (status);
1161 }
1162 
1163 static dladm_status_t
1164 do_get_radio(datalink_id_t linkid, wldp_t *gbuf)
1165 {
1166 	return (i_dladm_wlan_get_ioctl(linkid, gbuf, WL_RADIO));
1167 }
1168 
1169 static dladm_status_t
1170 do_get_radio_prop(datalink_id_t linkid, char **prop_val, uint_t *val_cnt)
1171 {
1172 	wl_radio_t	radio;
1173 	const char	*s;
1174 	wldp_t		*gbuf;
1175 	dladm_status_t	status = DLADM_STATUS_OK;
1176 
1177 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
1178 		return (DLADM_STATUS_NOMEM);
1179 
1180 	if ((status = do_get_radio(linkid, gbuf)) != DLADM_STATUS_OK)
1181 		goto done;
1182 
1183 	radio = *(wl_radio_t *)(gbuf->wldp_buf);
1184 	switch (radio) {
1185 	case B_TRUE:
1186 		s = "on";
1187 		break;
1188 	case B_FALSE:
1189 		s = "off";
1190 		break;
1191 	default:
1192 		status = DLADM_STATUS_NOTFOUND;
1193 		goto done;
1194 	}
1195 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
1196 	*val_cnt = 1;
1197 
1198 done:
1199 	free(gbuf);
1200 	return (status);
1201 }
1202 
1203 static dladm_status_t
1204 do_set_radio(datalink_id_t linkid, dladm_wlan_radio_t *radio)
1205 {
1206 	wl_radio_t r;
1207 
1208 	switch (*radio) {
1209 	case DLADM_WLAN_RADIO_ON:
1210 		r = B_TRUE;
1211 		break;
1212 	case DLADM_WLAN_RADIO_OFF:
1213 		r = B_FALSE;
1214 		break;
1215 	default:
1216 		return (DLADM_STATUS_NOTSUP);
1217 	}
1218 	return (i_dladm_wlan_set_ioctl(linkid, WL_RADIO, &r, sizeof (r)));
1219 }
1220 
1221 /* ARGSUSED */
1222 static dladm_status_t
1223 do_set_radio_prop(datalink_id_t linkid, val_desc_t *vdp, uint_t val_cnt)
1224 {
1225 	dladm_wlan_radio_t radio = (dladm_wlan_radio_t)vdp->vd_val;
1226 	dladm_status_t status;
1227 
1228 	if (val_cnt != 1)
1229 		return (DLADM_STATUS_BADVALCNT);
1230 
1231 	status = do_set_radio(linkid, &radio);
1232 
1233 	return (status);
1234 }
1235 
1236 static dladm_status_t
1237 i_dladm_set_linkprop_db(datalink_id_t linkid, const char *prop_name,
1238     char **prop_val, uint_t val_cnt)
1239 {
1240 	char		buf[MAXLINELEN];
1241 	int		i;
1242 	dladm_conf_t	conf;
1243 	dladm_status_t	status;
1244 
1245 	status = dladm_read_conf(linkid, &conf);
1246 	if (status != DLADM_STATUS_OK)
1247 		return (status);
1248 
1249 	/*
1250 	 * reset case.
1251 	 */
1252 	if (val_cnt == 0) {
1253 		status = dladm_unset_conf_field(conf, prop_name);
1254 		if (status == DLADM_STATUS_OK)
1255 			status = dladm_write_conf(conf);
1256 		goto done;
1257 	}
1258 
1259 	buf[0] = '\0';
1260 	for (i = 0; i < val_cnt; i++) {
1261 		(void) strlcat(buf, prop_val[i], MAXLINELEN);
1262 		if (i != val_cnt - 1)
1263 			(void) strlcat(buf, ",", MAXLINELEN);
1264 	}
1265 
1266 	status = dladm_set_conf_field(conf, prop_name, DLADM_TYPE_STR, buf);
1267 	if (status == DLADM_STATUS_OK)
1268 		status = dladm_write_conf(conf);
1269 
1270 done:
1271 	dladm_destroy_conf(conf);
1272 	return (status);
1273 }
1274 
1275 static dladm_status_t
1276 i_dladm_get_linkprop_db(datalink_id_t linkid, const char *prop_name,
1277     char **prop_val, uint_t *val_cntp)
1278 {
1279 	char		buf[MAXLINELEN], *str;
1280 	uint_t		cnt = 0;
1281 	dladm_conf_t	conf;
1282 	dladm_status_t	status;
1283 
1284 	status = dladm_read_conf(linkid, &conf);
1285 	if (status != DLADM_STATUS_OK)
1286 		return (status);
1287 
1288 	status = dladm_get_conf_field(conf, prop_name, buf, MAXLINELEN);
1289 	if (status != DLADM_STATUS_OK)
1290 		goto done;
1291 
1292 	str = strtok(buf, ",");
1293 	while (str != NULL) {
1294 		if (cnt == *val_cntp) {
1295 			status = DLADM_STATUS_TOOSMALL;
1296 			goto done;
1297 		}
1298 		(void) strlcpy(prop_val[cnt++], str, DLADM_PROP_VAL_MAX);
1299 		str = strtok(NULL, ",");
1300 	}
1301 
1302 	*val_cntp = cnt;
1303 
1304 done:
1305 	dladm_destroy_conf(conf);
1306 	return (status);
1307 }
1308