xref: /illumos-gate/usr/src/lib/libdladm/common/linkprop.c (revision 99dda20867d903eec23291ba1ecb18a82d70096b)
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 #include <libdladm.h>
50 #include <sys/param.h>
51 #include <sys/dld.h>
52 #include <inttypes.h>
53 #include <sys/ethernet.h>
54 
55 /*
56  * The linkprop get() callback.
57  * - pd: 	pointer to the struct prop_desc
58  * - propstrp:	a property string array to keep the returned property.
59  *		Caller allocated.
60  * - cntp:	number of returned properties.
61  *		Caller also uses it to indicate how many it expects.
62  */
63 struct prop_desc;
64 
65 typedef dladm_status_t	pd_getf_t(struct prop_desc *pd,
66 			datalink_id_t, char **propstp, uint_t *cntp,
67 			datalink_media_t);
68 
69 /*
70  * The linkprop set() callback.
71  * - propval:	a val_desc_t array which keeps the property values to be set.
72  * - cnt:	number of properties to be set.
73  * - flags: 	additional flags passed down the system call.
74  *
75  * pd_set takes val_desc_t given by pd_check(), translates it into
76  * a format suitable for kernel consumption. This may require allocation
77  * of ioctl buffers etc. pd_set() may call another common routine (used
78  * by all other pd_sets) which invokes the ioctl.
79  */
80 typedef dladm_status_t	pd_setf_t(struct prop_desc *, datalink_id_t,
81 			val_desc_t *propval, uint_t cnt, uint_t flags,
82 			datalink_media_t);
83 
84 
85 /*
86  * The linkprop check() callback.
87  * - propstrp:	property string array which keeps the property to be checked.
88  * - cnt:	number of properties.
89  * - propval:	return value; the property values of the given property strings.
90  *
91  * pd_check checks that the input values are valid. It does so by
92  * iteraring through the pd_modval list for the property. If
93  * the modifiable values cannot be expressed as a list, a pd_check
94  * specific to this property can be used. If the input values are
95  * verified to be valid, pd_check allocates a val_desc_t and fills it
96  * with either a val_desc_t found on the pd_modval list or something
97  * generated on the fly.
98  */
99 typedef dladm_status_t	pd_checkf_t(struct prop_desc *pd,
100 			    datalink_id_t, char **propstrp,
101 			    uint_t cnt, val_desc_t *propval,
102 			    datalink_media_t);
103 
104 typedef struct dld_public_prop_s {
105 	dld_prop_id_t	pp_id;
106 	size_t		pp_valsize;
107 	char		*pp_name;
108 	char		*pp_desc;
109 } dld_public_prop_t;
110 
111 static dld_ioc_prop_t *dld_buf_alloc(size_t, datalink_id_t, const char *,
112 					dladm_status_t *);
113 static dladm_status_t dld_set_prop(datalink_id_t, const char *, char **,
114 					uint_t, uint_t);
115 static dladm_status_t dld_get_prop(datalink_id_t, const char *, char **,
116 					uint_t *, dladm_prop_type_t);
117 static pd_getf_t	do_get_zone, do_get_autopush, do_get_rate_mod,
118 			do_get_rate_prop, do_get_channel_prop,
119 			do_get_powermode_prop, do_get_radio_prop,
120 			dld_duplex_get, dld_status_get,
121 			dld_binary_get, dld_uint64_get, dld_flowctl_get;
122 static pd_setf_t	do_set_zone, do_set_autopush, do_set_rate_prop,
123 			do_set_powermode_prop, do_set_radio_prop,
124 			dld_set_public_prop;
125 static pd_checkf_t	do_check_zone, do_check_autopush, do_check_rate,
126 			dld_defmtu_check;
127 
128 static dladm_status_t	dld_speed_get(struct prop_desc *, datalink_id_t,
129 			char **, uint_t *);
130 
131 typedef struct prop_desc {
132 	/*
133 	 * link property name
134 	 */
135 	char			*pd_name;
136 
137 	/*
138 	 * default property value, can be set to { "", NULL }
139 	 */
140 	val_desc_t		pd_defval;
141 
142 	/*
143 	 * list of optional property values, can be NULL.
144 	 *
145 	 * This is set to non-NULL if there is a list of possible property
146 	 * values.  pd_optval would point to the array of possible values.
147 	 */
148 	val_desc_t		*pd_optval;
149 
150 	/*
151 	 * count of the above optional property values. 0 if pd_optval is NULL.
152 	 */
153 	uint_t			pd_noptval;
154 
155 	/*
156 	 * callback to set link property;
157 	 * set to NULL if this property is read-only
158 	 */
159 	pd_setf_t		*pd_set;
160 
161 	/*
162 	 * callback to get modifiable link property
163 	 */
164 	pd_getf_t		*pd_getmod;
165 
166 	/*
167 	 * callback to get current link property
168 	 */
169 	pd_getf_t		*pd_get;
170 
171 	/*
172 	 * callback to validate link property value, set to NULL if pd_optval
173 	 * is not NULL. In that case, validate the value by comparing it with
174 	 * the pd_optval. Return a val_desc_t array pointer if the value is
175 	 * valid.
176 	 */
177 	pd_checkf_t		*pd_check;
178 
179 	uint_t			pd_flags;
180 #define	PD_TEMPONLY	0x1	/* property is temporary only */
181 #define	PD_CHECK_ALLOC	0x2	/* alloc vd_val as part of pd_check */
182 	/*
183 	 * indicate link classes this property applies to.
184 	 */
185 	datalink_class_t	pd_class;
186 
187 	/*
188 	 * indicate link media type this property applies to.
189 	 */
190 	datalink_media_t	pd_dmedia;
191 } prop_desc_t;
192 
193 #define	DLD_PROPBUF_SIZE(v)	sizeof (dld_ioc_prop_t) + (v) - 1
194 
195 
196 static dld_public_prop_t dld_prop[] = {
197 	{ DLD_PROP_DUPLEX,	sizeof (uint8_t),
198 	    "duplex",		"link duplex mode" },
199 
200 	{DLD_PROP_SPEED,	sizeof (uint64_t),
201 	    "speed",		"link speed (bps)" },
202 
203 	{ DLD_PROP_STATUS,	sizeof (uint8_t),
204 	    "state",		"link up/down" },
205 
206 	{ DLD_PROP_AUTONEG,	sizeof (uint8_t),
207 	    "adv_autoneg_cap",	"Advertised auto-negotiation" },
208 
209 	{ DLD_PROP_DEFMTU,	sizeof (uint64_t),
210 	    "mtu",		"current link mtu" },
211 
212 	{ DLD_PROP_FLOWCTRL,	sizeof (link_flowctrl_t),
213 	    "flowctrl",		"flowcontrol" },
214 
215 	{ DLD_PROP_ADV_1000FDX_CAP, sizeof (uint8_t),
216 	    "adv_1000fdx_cap",	"Adv 1000 Mbps fdx" },
217 
218 	{ DLD_PROP_EN_1000FDX_CAP, sizeof (uint8_t),
219 	    "en_1000fdx_cap",	"Enable 1000 Mbps fdx" },
220 
221 	{ DLD_PROP_ADV_1000HDX_CAP, sizeof (uint8_t),
222 	    "adv_1000hdx_cap", "Adv 1000 Mbps hdx" },
223 
224 	{ DLD_PROP_EN_1000HDX_CAP, sizeof (uint8_t),
225 	    "en_1000hdx_cap",	"Enable 1000 Mbps hdx" },
226 
227 	{ DLD_PROP_ADV_100FDX_CAP, sizeof (uint8_t),
228 	    "adv_100fdx_cap",	"Adv 100 Mbps fdx" },
229 
230 	{ DLD_PROP_EN_100FDX_CAP, sizeof (uint8_t),
231 	    "en_100fdx_cap",	"Enable 100 Mbps fdx" },
232 
233 	{ DLD_PROP_ADV_100HDX_CAP, sizeof (uint8_t),
234 	    "adv_100hdx_cap",	"Adv 100 Mbps hdx" },
235 
236 	{ DLD_PROP_EN_100HDX_CAP, sizeof (uint8_t),
237 	    "en_100hdx_cap",	"Enable 100 Mbps hdx" },
238 
239 	{ DLD_PROP_ADV_10FDX_CAP, sizeof (uint8_t),
240 	    "adv_10fdx_cap",	"Adv 10 Mbps fdx" },
241 
242 	{ DLD_PROP_EN_10FDX_CAP, sizeof (uint8_t),
243 	    "en_10fdx_cap",	"Enable 10 Mbps fdx" },
244 
245 	{ DLD_PROP_ADV_10HDX_CAP, sizeof (uint8_t),
246 	    "adv_10hdx_cap",	"Adv 10 Mbps hdx" },
247 
248 	{ DLD_PROP_EN_10HDX_CAP, sizeof (uint8_t),
249 	    "en_10hdx_cap",	"Enable 10 Mbps hdx" },
250 
251 	{ DLD_PROP_PRIVATE, 0,
252 	    "driver-private",	"" }
253 };
254 
255 static  val_desc_t	link_duplex_vals[] = {
256 	{ "half", 	LINK_DUPLEX_HALF	},
257 	{ "full", 	LINK_DUPLEX_HALF	}
258 };
259 static  val_desc_t	link_speed_vals[] = {
260 	{ "10",		10			},
261 	{ "100",	100			},
262 	{ "1000",	1000			}
263 };
264 static  val_desc_t	link_status_vals[] = {
265 	{ "up",		LINK_STATE_UP		},
266 	{ "down",	LINK_STATE_DOWN		}
267 };
268 static  val_desc_t	link_01_vals[] = {
269 	{ "1",		1			},
270 	{ "0",		0			}
271 };
272 static  val_desc_t	link_flow_vals[] = {
273 	{ "no",		LINK_FLOWCTRL_NONE	},
274 	{ "tx",		LINK_FLOWCTRL_TX	},
275 	{ "rx",		LINK_FLOWCTRL_RX	},
276 	{ "bi",		LINK_FLOWCTRL_BI	}
277 };
278 static  val_desc_t	macdefaultmtu_vals[] = {
279 	{ "68-9000",	NULL			}
280 };
281 
282 #define	VALCNT(vals)    (sizeof ((vals)) / sizeof (val_desc_t))
283 
284 static val_desc_t	dladm_wlan_radio_vals[] = {
285 	{ "on",		DLADM_WLAN_RADIO_ON	},
286 	{ "off",	DLADM_WLAN_RADIO_OFF	}
287 };
288 
289 static val_desc_t	dladm_wlan_powermode_vals[] = {
290 	{ "off",	DLADM_WLAN_PM_OFF	},
291 	{ "fast",	DLADM_WLAN_PM_FAST	},
292 	{ "max",	DLADM_WLAN_PM_MAX	}
293 };
294 
295 static prop_desc_t	prop_table[] = {
296 
297 	{ "channel",	{ NULL, 0 },
298 	    NULL, 0, NULL, NULL,
299 	    do_get_channel_prop, NULL, 0,
300 	    DATALINK_CLASS_PHYS, DL_WIFI },
301 
302 	{ "powermode",	{ "off", DLADM_WLAN_PM_OFF },
303 	    dladm_wlan_powermode_vals, VALCNT(dladm_wlan_powermode_vals),
304 	    do_set_powermode_prop, NULL,
305 	    do_get_powermode_prop, NULL, 0,
306 	    DATALINK_CLASS_PHYS, DL_WIFI },
307 
308 	{ "radio",	{ "on", DLADM_WLAN_RADIO_ON },
309 	    dladm_wlan_radio_vals, VALCNT(dladm_wlan_radio_vals),
310 	    do_set_radio_prop, NULL,
311 	    do_get_radio_prop, NULL, 0,
312 	    DATALINK_CLASS_PHYS, DL_WIFI },
313 
314 	{ "speed",	{ "", 0 }, NULL, 0,
315 	    do_set_rate_prop, do_get_rate_mod,
316 	    do_get_rate_prop, do_check_rate, 0,
317 	    DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE },
318 
319 	{ "autopush",	{ "", NULL }, NULL, 0,
320 	    do_set_autopush, NULL,
321 	    do_get_autopush, do_check_autopush, PD_CHECK_ALLOC,
322 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
323 
324 	{ "zone",	{ "", NULL }, NULL, 0,
325 	    do_set_zone, NULL,
326 	    do_get_zone, do_check_zone, PD_TEMPONLY,
327 	    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE },
328 
329 	{ "duplex", { "full", LINK_DUPLEX_FULL },
330 	    link_duplex_vals, VALCNT(link_duplex_vals),
331 	    NULL, NULL, dld_duplex_get, NULL,
332 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
333 
334 	{ "state", { "up", LINK_STATE_UP },
335 	    link_status_vals, VALCNT(link_status_vals),
336 	    NULL, NULL, dld_status_get, NULL,
337 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
338 
339 	{ "adv_autoneg_cap", { "1", 1 },
340 	    link_01_vals, VALCNT(link_01_vals),
341 	    dld_set_public_prop, NULL, dld_binary_get, NULL,
342 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
343 
344 	{ "mtu", { NULL, NULL },
345 	    macdefaultmtu_vals, VALCNT(macdefaultmtu_vals),
346 	    dld_set_public_prop, NULL, dld_uint64_get, dld_defmtu_check,
347 	    PD_CHECK_ALLOC, DATALINK_CLASS_PHYS, DL_ETHER },
348 
349 	{ "flowctrl", { "bi", LINK_FLOWCTRL_BI },
350 	    link_flow_vals, VALCNT(link_flow_vals),
351 	    dld_set_public_prop, NULL, dld_flowctl_get, NULL,
352 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
353 
354 	{ "adv_1000fdx_cap", { NULL, NULL },
355 	    link_01_vals, 0,
356 	    NULL, NULL, dld_binary_get, NULL,
357 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
358 
359 	{ "en_1000fdx_cap", { NULL, NULL },
360 	    link_01_vals, VALCNT(link_01_vals),
361 	    dld_set_public_prop, NULL, dld_binary_get, NULL,
362 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
363 
364 	{ "adv_1000hdx_cap", { NULL, NULL },
365 	    link_01_vals, VALCNT(link_01_vals),
366 	    NULL, NULL, dld_binary_get, NULL,
367 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
368 
369 	{ "en_1000hdx_cap", { NULL, NULL },
370 	    link_01_vals, VALCNT(link_01_vals),
371 	    dld_set_public_prop, NULL, dld_binary_get, NULL,
372 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
373 
374 	{ "adv_100fdx_cap", { NULL, NULL },
375 	    link_01_vals, VALCNT(link_01_vals),
376 	    NULL, NULL, dld_binary_get, NULL,
377 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
378 
379 	{ "en_100fdx_cap", { NULL, NULL },
380 	    link_01_vals, VALCNT(link_01_vals),
381 	    dld_set_public_prop, NULL, dld_binary_get, NULL,
382 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
383 
384 	{ "adv_100hdx_cap", { NULL, NULL },
385 	    link_01_vals, VALCNT(link_01_vals),
386 	    NULL, NULL, dld_binary_get, NULL,
387 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
388 
389 	{ "en_100hdx_cap", { NULL, NULL },
390 	    link_01_vals, VALCNT(link_01_vals),
391 	    dld_set_public_prop, NULL, dld_binary_get, NULL,
392 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
393 
394 	{ "adv_10fdx_cap", { NULL, NULL },
395 	    link_01_vals, VALCNT(link_01_vals),
396 	    NULL, NULL, dld_binary_get, NULL,
397 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
398 
399 	{ "en_10fdx_cap", { NULL, NULL },
400 	    link_01_vals, VALCNT(link_01_vals),
401 	    dld_set_public_prop, NULL, dld_binary_get, NULL,
402 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
403 
404 	{ "adv_10hdx_cap", { NULL, NULL },
405 	    link_01_vals, VALCNT(link_01_vals),
406 	    NULL, NULL, dld_binary_get, NULL,
407 	    0, DATALINK_CLASS_PHYS, DL_ETHER },
408 
409 	{ "en_10hdx_cap", { NULL, NULL },
410 	    link_01_vals, VALCNT(link_01_vals),
411 	    dld_set_public_prop, NULL, dld_binary_get, NULL,
412 	    0, DATALINK_CLASS_PHYS, DL_ETHER }
413 
414 };
415 
416 #define	DLADM_MAX_PROPS	(sizeof (prop_table) / sizeof (prop_desc_t))
417 
418 static dladm_status_t	i_dladm_set_linkprop_db(datalink_id_t, const char *,
419 			    char **, uint_t);
420 static dladm_status_t	i_dladm_get_linkprop_db(datalink_id_t, const char *,
421 			    char **, uint_t *);
422 static dladm_status_t	i_dladm_set_single_prop(datalink_id_t, datalink_class_t,
423 			    uint32_t, prop_desc_t *, char **, uint_t, uint_t);
424 static dladm_status_t	i_dladm_set_linkprop(datalink_id_t, const char *,
425 			    char **, uint_t, uint_t);
426 
427 /*
428  * Unfortunately, MAX_SCAN_SUPPORT_RATES is too small to allow all
429  * rates to be retrieved. However, we cannot increase it at this
430  * time because it will break binary compatibility with unbundled
431  * WiFi drivers and utilities. So for now we define an additional
432  * constant, MAX_SUPPORT_RATES, to allow all rates to be retrieved.
433  */
434 #define	MAX_SUPPORT_RATES	64
435 
436 #define	AP_ANCHOR	"[anchor]"
437 #define	AP_DELIMITER	'.'
438 
439 static dladm_status_t
440 do_check_prop(prop_desc_t *pdp, char **prop_val, uint_t val_cnt,
441     val_desc_t *vdp)
442 {
443 	int		i, j;
444 	dladm_status_t	status = DLADM_STATUS_OK;
445 
446 	for (j = 0; j < val_cnt; j++) {
447 		for (i = 0; i < pdp->pd_noptval; i++) {
448 			if (strcasecmp(*prop_val,
449 			    pdp->pd_optval[i].vd_name) == 0) {
450 				break;
451 			}
452 		}
453 		if (i == pdp->pd_noptval) {
454 			status = DLADM_STATUS_BADVAL;
455 			goto done;
456 		}
457 		(void) memcpy(vdp + j, &pdp->pd_optval[i], sizeof (val_desc_t));
458 	}
459 
460 done:
461 	return (status);
462 }
463 
464 static dladm_status_t
465 i_dladm_set_single_prop(datalink_id_t linkid, datalink_class_t class,
466     uint32_t media, prop_desc_t *pdp, char **prop_val, uint_t val_cnt,
467     uint_t flags)
468 {
469 	dladm_status_t	status = DLADM_STATUS_OK;
470 	val_desc_t	*vdp = NULL;
471 	boolean_t	needfree = B_FALSE;
472 	uint_t		cnt, i;
473 
474 	if (!(pdp->pd_class & class))
475 		return (DLADM_STATUS_BADARG);
476 
477 	if (!DATALINK_MEDIA_ACCEPTED(pdp->pd_dmedia, media))
478 		return (DLADM_STATUS_BADARG);
479 
480 	if ((flags & DLADM_OPT_PERSIST) && (pdp->pd_flags & PD_TEMPONLY))
481 		return (DLADM_STATUS_TEMPONLY);
482 
483 	if (!(flags & DLADM_OPT_ACTIVE))
484 		return (DLADM_STATUS_OK);
485 
486 	if (pdp->pd_set == NULL)
487 		return (DLADM_STATUS_PROPRDONLY);
488 
489 	if (pdp->pd_flags & PD_CHECK_ALLOC)
490 		needfree = B_TRUE;
491 	else
492 		needfree = B_FALSE;
493 	if (prop_val != NULL) {
494 		vdp = malloc(sizeof (val_desc_t) * val_cnt);
495 		if (vdp == NULL)
496 			return (DLADM_STATUS_NOMEM);
497 
498 
499 		if (pdp->pd_check != NULL) {
500 			status = pdp->pd_check(pdp, linkid, prop_val, val_cnt,
501 			    vdp, media);
502 		} else if (pdp->pd_optval != NULL) {
503 			status = do_check_prop(pdp, prop_val, val_cnt, vdp);
504 		} else {
505 			status = DLADM_STATUS_BADARG;
506 		}
507 
508 		if (status != DLADM_STATUS_OK)
509 			goto done;
510 
511 		cnt = val_cnt;
512 	} else {
513 		if (pdp->pd_defval.vd_name == NULL)
514 			return (DLADM_STATUS_NOTSUP);
515 
516 		if ((vdp = malloc(sizeof (val_desc_t))) == NULL)
517 			return (DLADM_STATUS_NOMEM);
518 
519 		(void) memcpy(vdp, &pdp->pd_defval, sizeof (val_desc_t));
520 		cnt = 1;
521 	}
522 	status = pdp->pd_set(pdp, linkid, vdp, cnt, flags, media);
523 	if (needfree) {
524 		for (i = 0; i < cnt; i++)
525 			free((void *)((val_desc_t *)vdp + i)->vd_val);
526 	}
527 done:
528 	free(vdp);
529 	return (status);
530 }
531 
532 static dladm_status_t
533 i_dladm_set_linkprop(datalink_id_t linkid, const char *prop_name,
534     char **prop_val, uint_t val_cnt, uint_t flags)
535 {
536 	int			i;
537 	boolean_t		found = B_FALSE;
538 	datalink_class_t	class;
539 	uint32_t		media;
540 	dladm_status_t		status = DLADM_STATUS_OK;
541 
542 	status = dladm_datalink_id2info(linkid, NULL, &class, &media, NULL, 0);
543 	if (status != DLADM_STATUS_OK)
544 		return (status);
545 
546 	for (i = 0; i < DLADM_MAX_PROPS; i++) {
547 		prop_desc_t	*pdp = &prop_table[i];
548 		dladm_status_t	s;
549 
550 		if (prop_name != NULL &&
551 		    (strcasecmp(prop_name, pdp->pd_name) != 0))
552 			continue;
553 
554 		found = B_TRUE;
555 		s = i_dladm_set_single_prop(linkid, class, media, pdp, prop_val,
556 		    val_cnt, flags);
557 
558 		if (prop_name != NULL) {
559 			status = s;
560 			break;
561 		} else {
562 			if (s != DLADM_STATUS_OK &&
563 			    s != DLADM_STATUS_NOTSUP)
564 				status = s;
565 		}
566 	}
567 	if (!found) {
568 		if (prop_name[0] == '_') {
569 			/* other private properties */
570 			status = dld_set_prop(linkid, prop_name, prop_val,
571 			    val_cnt, flags);
572 		} else  {
573 			status = DLADM_STATUS_NOTFOUND;
574 		}
575 	}
576 
577 	return (status);
578 }
579 
580 /*
581  * Set/reset link property for specific link
582  */
583 dladm_status_t
584 dladm_set_linkprop(datalink_id_t linkid, const char *prop_name, char **prop_val,
585     uint_t val_cnt, uint_t flags)
586 {
587 	dladm_status_t	status = DLADM_STATUS_OK;
588 
589 	if ((linkid == DATALINK_INVALID_LINKID) || (flags == 0) ||
590 	    (prop_val == NULL && val_cnt > 0) ||
591 	    (prop_val != NULL && val_cnt == 0) ||
592 	    (prop_name == NULL && prop_val != NULL)) {
593 		return (DLADM_STATUS_BADARG);
594 	}
595 
596 	status = i_dladm_set_linkprop(linkid, prop_name, prop_val,
597 	    val_cnt, flags);
598 	if (status != DLADM_STATUS_OK)
599 		return (status);
600 
601 	if (flags & DLADM_OPT_PERSIST) {
602 		status = i_dladm_set_linkprop_db(linkid, prop_name,
603 		    prop_val, val_cnt);
604 	}
605 	return (status);
606 }
607 
608 /*
609  * Walk link properties of the given specific link.
610  */
611 dladm_status_t
612 dladm_walk_linkprop(datalink_id_t linkid, void *arg,
613     int (*func)(datalink_id_t, const char *, void *))
614 {
615 	dladm_status_t		status;
616 	datalink_class_t	class;
617 	uint_t			media;
618 	int			i;
619 
620 	if (linkid == DATALINK_INVALID_LINKID || func == NULL)
621 		return (DLADM_STATUS_BADARG);
622 
623 	status = dladm_datalink_id2info(linkid, NULL, &class, &media, NULL, 0);
624 	if (status != DLADM_STATUS_OK)
625 		return (status);
626 
627 	for (i = 0; i < DLADM_MAX_PROPS; i++) {
628 		if (!(prop_table[i].pd_class & class))
629 			continue;
630 
631 		if (!DATALINK_MEDIA_ACCEPTED(prop_table[i].pd_dmedia, media))
632 			continue;
633 
634 		if (func(linkid, prop_table[i].pd_name, arg) ==
635 		    DLADM_WALK_TERMINATE) {
636 			break;
637 		}
638 	}
639 
640 	return (DLADM_STATUS_OK);
641 }
642 
643 /*
644  * Get linkprop of the given specific link.
645  */
646 dladm_status_t
647 dladm_get_linkprop(datalink_id_t linkid, dladm_prop_type_t type,
648     const char *prop_name, char **prop_val, uint_t *val_cntp)
649 {
650 	dladm_status_t		status = DLADM_STATUS_OK;
651 	datalink_class_t	class;
652 	uint_t			media;
653 	prop_desc_t		*pdp;
654 	uint_t			cnt;
655 	int			i;
656 
657 	if (linkid == DATALINK_INVALID_LINKID || prop_name == NULL ||
658 	    prop_val == NULL || val_cntp == NULL || *val_cntp == 0)
659 		return (DLADM_STATUS_BADARG);
660 
661 	for (i = 0; i < DLADM_MAX_PROPS; i++)
662 		if (strcasecmp(prop_name, prop_table[i].pd_name) == 0)
663 			break;
664 
665 	if (i == DLADM_MAX_PROPS) {
666 		if (prop_name[0] == '_') {
667 			/*
668 			 * private property.
669 			 */
670 			return (dld_get_prop(linkid, prop_name,
671 			    prop_val, val_cntp, type));
672 		} else {
673 			return (DLADM_STATUS_NOTFOUND);
674 		}
675 	}
676 
677 	pdp = &prop_table[i];
678 
679 	status = dladm_datalink_id2info(linkid, NULL, &class, &media, NULL, 0);
680 	if (status != DLADM_STATUS_OK)
681 		return (status);
682 
683 	if (!(pdp->pd_class & class))
684 		return (DLADM_STATUS_BADARG);
685 
686 	if (!DATALINK_MEDIA_ACCEPTED(pdp->pd_dmedia, media))
687 		return (DLADM_STATUS_BADARG);
688 
689 	switch (type) {
690 	case DLADM_PROP_VAL_CURRENT:
691 		status = pdp->pd_get(pdp, linkid, prop_val, val_cntp, media);
692 		break;
693 
694 	case DLADM_PROP_VAL_DEFAULT:
695 		if (pdp->pd_defval.vd_name == NULL) {
696 			status = DLADM_STATUS_NOTSUP;
697 			break;
698 		}
699 		(void) strcpy(*prop_val, pdp->pd_defval.vd_name);
700 		*val_cntp = 1;
701 		break;
702 
703 	case DLADM_PROP_VAL_MODIFIABLE:
704 		if (pdp->pd_getmod != NULL) {
705 			status = pdp->pd_getmod(pdp, linkid, prop_val,
706 			    val_cntp, media);
707 			break;
708 		}
709 		cnt = pdp->pd_noptval;
710 		if (cnt == 0) {
711 			status = DLADM_STATUS_NOTSUP;
712 		} else if (cnt > *val_cntp) {
713 			status = DLADM_STATUS_TOOSMALL;
714 		} else {
715 			for (i = 0; i < cnt; i++) {
716 				(void) strcpy(prop_val[i],
717 				    pdp->pd_optval[i].vd_name);
718 			}
719 			*val_cntp = cnt;
720 		}
721 		break;
722 	case DLADM_PROP_VAL_PERSISTENT:
723 		if (pdp->pd_flags & PD_TEMPONLY)
724 			return (DLADM_STATUS_TEMPONLY);
725 		status = i_dladm_get_linkprop_db(linkid, prop_name,
726 		    prop_val, val_cntp);
727 		break;
728 	default:
729 		status = DLADM_STATUS_BADARG;
730 		break;
731 	}
732 
733 	return (status);
734 }
735 
736 /*ARGSUSED*/
737 static int
738 i_dladm_init_one_prop(datalink_id_t linkid, const char *prop_name, void *arg)
739 {
740 	char	*buf, **propvals;
741 	uint_t	i, valcnt = DLADM_MAX_PROP_VALCNT;
742 
743 	if ((buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) *
744 	    DLADM_MAX_PROP_VALCNT)) == NULL) {
745 		return (DLADM_WALK_CONTINUE);
746 	}
747 
748 	propvals = (char **)(void *)buf;
749 	for (i = 0; i < valcnt; i++) {
750 		propvals[i] = buf +
751 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
752 		    i * DLADM_PROP_VAL_MAX;
753 	}
754 
755 	if (dladm_get_linkprop(linkid, DLADM_PROP_VAL_PERSISTENT, prop_name,
756 	    propvals, &valcnt) != DLADM_STATUS_OK) {
757 		goto done;
758 	}
759 
760 	(void) dladm_set_linkprop(linkid, prop_name, propvals, valcnt,
761 	    DLADM_OPT_ACTIVE);
762 
763 done:
764 	if (buf != NULL)
765 		free(buf);
766 
767 	return (DLADM_WALK_CONTINUE);
768 }
769 
770 /*ARGSUSED*/
771 static int
772 i_dladm_init_linkprop(datalink_id_t linkid, void *arg)
773 {
774 	(void) dladm_init_linkprop(linkid);
775 	return (DLADM_WALK_CONTINUE);
776 }
777 
778 dladm_status_t
779 dladm_init_linkprop(datalink_id_t linkid)
780 {
781 	if (linkid == DATALINK_ALL_LINKID) {
782 		(void) dladm_walk_datalink_id(i_dladm_init_linkprop, NULL,
783 		    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
784 		    DLADM_OPT_PERSIST);
785 	} else {
786 		(void) dladm_walk_linkprop(linkid, NULL, i_dladm_init_one_prop);
787 	}
788 	return (DLADM_STATUS_OK);
789 }
790 
791 /* ARGSUSED */
792 static dladm_status_t
793 do_get_zone(struct prop_desc *pd, datalink_id_t linkid,
794     char **prop_val, uint_t *val_cnt, datalink_media_t media)
795 {
796 	char		zone_name[ZONENAME_MAX];
797 	zoneid_t	zid;
798 	dladm_status_t	status;
799 
800 	status = dladm_getzid(linkid, &zid);
801 	if (status != DLADM_STATUS_OK)
802 		return (status);
803 
804 	*val_cnt = 1;
805 	if (zid != GLOBAL_ZONEID) {
806 		if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0)
807 			return (dladm_errno2status(errno));
808 
809 		(void) strncpy(*prop_val, zone_name, DLADM_PROP_VAL_MAX);
810 	} else {
811 		*prop_val[0] = '\0';
812 	}
813 
814 	return (DLADM_STATUS_OK);
815 }
816 
817 typedef int (*zone_get_devroot_t)(char *, char *, size_t);
818 
819 static int
820 i_dladm_get_zone_dev(char *zone_name, char *dev, size_t devlen)
821 {
822 	char			root[MAXPATHLEN];
823 	zone_get_devroot_t	real_zone_get_devroot;
824 	void			*dlhandle;
825 	void			*sym;
826 	int			ret;
827 
828 	if ((dlhandle = dlopen("libzonecfg.so.1", RTLD_LAZY)) == NULL)
829 		return (-1);
830 
831 	if ((sym = dlsym(dlhandle, "zone_get_devroot")) == NULL) {
832 		(void) dlclose(dlhandle);
833 		return (-1);
834 	}
835 
836 	real_zone_get_devroot = (zone_get_devroot_t)sym;
837 
838 	if ((ret = real_zone_get_devroot(zone_name, root, sizeof (root))) == 0)
839 		(void) snprintf(dev, devlen, "%s%s", root, "/dev");
840 	(void) dlclose(dlhandle);
841 	return (ret);
842 }
843 
844 static dladm_status_t
845 i_dladm_update_deventry(zoneid_t zid, datalink_id_t linkid, boolean_t add)
846 {
847 	char		path[MAXPATHLEN];
848 	char		name[MAXLINKNAMELEN];
849 	di_prof_t	prof = NULL;
850 	char		zone_name[ZONENAME_MAX];
851 	dladm_status_t	status;
852 	int		ret;
853 
854 	if (getzonenamebyid(zid, zone_name, sizeof (zone_name)) < 0)
855 		return (dladm_errno2status(errno));
856 	if (i_dladm_get_zone_dev(zone_name, path, sizeof (path)) != 0)
857 		return (dladm_errno2status(errno));
858 	if (di_prof_init(path, &prof) != 0)
859 		return (dladm_errno2status(errno));
860 
861 	status = dladm_linkid2legacyname(linkid, name, MAXLINKNAMELEN);
862 	if (status != DLADM_STATUS_OK)
863 		goto cleanup;
864 
865 	if (add)
866 		ret = di_prof_add_dev(prof, name);
867 	else
868 		ret = di_prof_add_exclude(prof, name);
869 
870 	if (ret != 0) {
871 		status = dladm_errno2status(errno);
872 		goto cleanup;
873 	}
874 
875 	if (di_prof_commit(prof) != 0)
876 		status = dladm_errno2status(errno);
877 cleanup:
878 	if (prof)
879 		di_prof_fini(prof);
880 
881 	return (status);
882 }
883 
884 /* ARGSUSED */
885 static dladm_status_t
886 do_set_zone(prop_desc_t *pd, datalink_id_t linkid, val_desc_t *vdp,
887     uint_t val_cnt, uint_t flags, datalink_media_t media)
888 {
889 	dladm_status_t	status;
890 	zoneid_t	zid_old, zid_new;
891 	char		link[MAXLINKNAMELEN];
892 
893 	if (val_cnt != 1)
894 		return (DLADM_STATUS_BADVALCNT);
895 
896 	status = dladm_getzid(linkid, &zid_old);
897 	if (status != DLADM_STATUS_OK)
898 		return (status);
899 
900 	/* Do nothing if setting to current value */
901 	zid_new = vdp->vd_val;
902 	if (zid_new == zid_old)
903 		return (DLADM_STATUS_OK);
904 
905 	if ((status = dladm_datalink_id2info(linkid, NULL, NULL, NULL,
906 	    link, MAXLINKNAMELEN)) != DLADM_STATUS_OK) {
907 		return (status);
908 	}
909 
910 	if (zid_new != GLOBAL_ZONEID) {
911 		/*
912 		 * If the new zoneid is the global zone, we could destroy
913 		 * the link (in the case of an implicitly-created VLAN) as a
914 		 * result of the dladm_setzid() operation. In that case,
915 		 * we defer the operation to the end of this function to avoid
916 		 * recreating the VLAN and getting a different linkid during
917 		 * the rollback if other operation fails.
918 		 *
919 		 * Otherwise, dladm_setzid() will hold a reference to the
920 		 * link and prevent a link renaming, so we need to do it
921 		 * before other operations.
922 		 */
923 		status = dladm_setzid(link, zid_new);
924 		if (status != DLADM_STATUS_OK)
925 			return (status);
926 	}
927 
928 	if (zid_old != GLOBAL_ZONEID) {
929 		if (zone_remove_datalink(zid_old, link) != 0 &&
930 		    errno != ENXIO) {
931 			status = dladm_errno2status(errno);
932 			goto rollback1;
933 		}
934 
935 		/*
936 		 * It is okay to fail to update the /dev entry (some
937 		 * vanity-named links do not have a /dev entry).
938 		 */
939 		(void) i_dladm_update_deventry(zid_old, linkid, B_FALSE);
940 	}
941 
942 	if (zid_new != GLOBAL_ZONEID) {
943 		if (zone_add_datalink(zid_new, link) != 0) {
944 			status = dladm_errno2status(errno);
945 			goto rollback2;
946 		}
947 
948 		(void) i_dladm_update_deventry(zid_new, linkid, B_TRUE);
949 	} else {
950 		status = dladm_setzid(link, zid_new);
951 		if (status != DLADM_STATUS_OK)
952 			goto rollback2;
953 	}
954 
955 	return (DLADM_STATUS_OK);
956 
957 rollback2:
958 	if (zid_old != GLOBAL_ZONEID)
959 		(void) i_dladm_update_deventry(zid_old, linkid, B_TRUE);
960 	if (zid_old != GLOBAL_ZONEID)
961 		(void) zone_add_datalink(zid_old, link);
962 rollback1:
963 	if (zid_new != GLOBAL_ZONEID)
964 		(void) dladm_setzid(link, zid_old);
965 	return (status);
966 }
967 
968 /* ARGSUSED */
969 static dladm_status_t
970 do_check_zone(struct prop_desc *pd, datalink_id_t linkid, char **prop_val,
971     uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
972 {
973 	zoneid_t	zid;
974 
975 	if (val_cnt != 1)
976 		return (DLADM_STATUS_BADVALCNT);
977 
978 	if ((zid = getzoneidbyname(*prop_val)) == -1)
979 		return (DLADM_STATUS_BADVAL);
980 
981 	if (zid != GLOBAL_ZONEID) {
982 		ushort_t	flags;
983 
984 		if (zone_getattr(zid, ZONE_ATTR_FLAGS, &flags,
985 		    sizeof (flags)) < 0) {
986 			return (dladm_errno2status(errno));
987 		}
988 
989 		if (!(flags & ZF_NET_EXCL)) {
990 			return (DLADM_STATUS_BADVAL);
991 		}
992 	}
993 
994 	vdp->vd_val = zid;
995 	return (DLADM_STATUS_OK);
996 }
997 
998 /* ARGSUSED */
999 static dladm_status_t
1000 do_get_autopush(struct prop_desc *pd, datalink_id_t linkid,
1001     char **prop_val, uint_t *val_cnt, datalink_media_t media)
1002 {
1003 	dld_ioc_ap_t	dia;
1004 	int		fd, i, len;
1005 
1006 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
1007 		return (dladm_errno2status(errno));
1008 
1009 	*val_cnt = 1;
1010 	dia.dia_linkid = linkid;
1011 	if (i_dladm_ioctl(fd, DLDIOC_GETAUTOPUSH, &dia, sizeof (dia)) < 0) {
1012 		(*prop_val)[0] = '\0';
1013 		goto done;
1014 	}
1015 
1016 	for (i = 0, len = 0; i < dia.dia_npush; i++) {
1017 		if (i != 0) {
1018 			(void) snprintf(*prop_val + len,
1019 			    DLADM_PROP_VAL_MAX - len, "%c", AP_DELIMITER);
1020 			len += 1;
1021 		}
1022 		(void) snprintf(*prop_val + len, DLADM_PROP_VAL_MAX - len,
1023 		    "%s", dia.dia_aplist[i]);
1024 		len += strlen(dia.dia_aplist[i]);
1025 		if (dia.dia_anchor - 1 == i) {
1026 			(void) snprintf(*prop_val + len,
1027 			    DLADM_PROP_VAL_MAX - len, "%c%s", AP_DELIMITER,
1028 			    AP_ANCHOR);
1029 			len += (strlen(AP_ANCHOR) + 1);
1030 		}
1031 	}
1032 
1033 done:
1034 	(void) close(fd);
1035 	return (DLADM_STATUS_OK);
1036 }
1037 
1038 /* ARGSUSED */
1039 static dladm_status_t
1040 do_set_autopush(prop_desc_t *pd, datalink_id_t linkid, val_desc_t *vdp,
1041     uint_t val_cnt, uint_t flags, datalink_media_t media)
1042 {
1043 	dld_ioc_ap_t		dia;
1044 	struct dlautopush	*dlap = (struct dlautopush *)vdp->vd_val;
1045 	dladm_status_t		status = DLADM_STATUS_OK;
1046 	int			fd, i;
1047 	int			ic_cmd;
1048 
1049 	if (val_cnt != 1)
1050 		return (DLADM_STATUS_BADVALCNT);
1051 
1052 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
1053 		return (dladm_errno2status(errno));
1054 
1055 	dia.dia_linkid = linkid;
1056 	if (dlap != NULL) {
1057 		dia.dia_anchor = dlap->dap_anchor;
1058 		dia.dia_npush = dlap->dap_npush;
1059 		for (i = 0; i < dia.dia_npush; i++) {
1060 			(void) strlcpy(dia.dia_aplist[i], dlap->dap_aplist[i],
1061 			    FMNAMESZ+1);
1062 		}
1063 		ic_cmd = DLDIOC_SETAUTOPUSH;
1064 	} else {
1065 		ic_cmd = DLDIOC_CLRAUTOPUSH;
1066 	}
1067 
1068 	if (i_dladm_ioctl(fd, ic_cmd, &dia, sizeof (dia)) < 0)
1069 		status = dladm_errno2status(errno);
1070 
1071 	(void) close(fd);
1072 	return (status);
1073 }
1074 
1075 /*
1076  * Add the specified module to the dlautopush structure; returns a
1077  * DLADM_STATUS_* code.
1078  */
1079 dladm_status_t
1080 i_dladm_add_ap_module(const char *module, struct dlautopush *dlap)
1081 {
1082 	if ((strlen(module) == 0) || (strlen(module) > FMNAMESZ))
1083 		return (DLADM_STATUS_BADVAL);
1084 
1085 	if (strncasecmp(module, AP_ANCHOR, strlen(AP_ANCHOR)) == 0) {
1086 		/*
1087 		 * We don't allow multiple anchors, and the anchor must
1088 		 * be after at least one module.
1089 		 */
1090 		if (dlap->dap_anchor != 0)
1091 			return (DLADM_STATUS_BADVAL);
1092 		if (dlap->dap_npush == 0)
1093 			return (DLADM_STATUS_BADVAL);
1094 
1095 		dlap->dap_anchor = dlap->dap_npush;
1096 		return (DLADM_STATUS_OK);
1097 	}
1098 	if (dlap->dap_npush > MAXAPUSH)
1099 		return (DLADM_STATUS_BADVALCNT);
1100 
1101 	(void) strlcpy(dlap->dap_aplist[dlap->dap_npush++], module,
1102 	    FMNAMESZ + 1);
1103 
1104 	return (DLADM_STATUS_OK);
1105 }
1106 
1107 /*
1108  * Currently, both '.' and ' '(space) can be used as the delimiters between
1109  * autopush modules. The former is used in dladm set-linkprop, and the
1110  * latter is used in the autopush(1M) file.
1111  */
1112 /* ARGSUSED */
1113 static dladm_status_t
1114 do_check_autopush(struct prop_desc *pd, datalink_id_t linkid, char **prop_val,
1115     uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
1116 {
1117 	char			*module;
1118 	struct dlautopush	*dlap;
1119 	dladm_status_t		status;
1120 	char			val[DLADM_PROP_VAL_MAX];
1121 	char			delimiters[4];
1122 
1123 	if (val_cnt != 1)
1124 		return (DLADM_STATUS_BADVALCNT);
1125 
1126 	dlap = malloc(sizeof (struct dlautopush));
1127 	if (dlap == NULL)
1128 		return (DLADM_STATUS_NOMEM);
1129 
1130 	(void) memset(dlap, 0, sizeof (struct dlautopush));
1131 	(void) snprintf(delimiters, 4, " %c\n", AP_DELIMITER);
1132 	bcopy(*prop_val, val, DLADM_PROP_VAL_MAX);
1133 	module = strtok(val, delimiters);
1134 	while (module != NULL) {
1135 		status = i_dladm_add_ap_module(module, dlap);
1136 		if (status != DLADM_STATUS_OK)
1137 			return (status);
1138 		module = strtok(NULL, delimiters);
1139 	}
1140 
1141 	vdp->vd_val = (uintptr_t)dlap;
1142 	return (DLADM_STATUS_OK);
1143 }
1144 
1145 /* ARGSUSED */
1146 static dladm_status_t
1147 do_get_rate_common(struct prop_desc *pd, datalink_id_t linkid,
1148     char **prop_val, uint_t *val_cnt, uint_t id)
1149 {
1150 	wl_rates_t	*wrp;
1151 	uint_t		i;
1152 	wldp_t		*gbuf = NULL;
1153 	dladm_status_t	status = DLADM_STATUS_OK;
1154 
1155 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL) {
1156 		status = DLADM_STATUS_NOMEM;
1157 		goto done;
1158 	}
1159 
1160 	status = i_dladm_wlan_get_ioctl(linkid, gbuf, id);
1161 	if (status != DLADM_STATUS_OK)
1162 		goto done;
1163 
1164 	wrp = (wl_rates_t *)gbuf->wldp_buf;
1165 	if (wrp->wl_rates_num > *val_cnt) {
1166 		status = DLADM_STATUS_TOOSMALL;
1167 		goto done;
1168 	}
1169 
1170 	if (wrp->wl_rates_rates[0] == 0) {
1171 		prop_val[0][0] = '\0';
1172 		*val_cnt = 1;
1173 		goto done;
1174 	}
1175 
1176 	for (i = 0; i < wrp->wl_rates_num; i++) {
1177 		(void) snprintf(prop_val[i], DLADM_STRSIZE, "%.*f",
1178 		    wrp->wl_rates_rates[i] % 2,
1179 		    (float)wrp->wl_rates_rates[i] / 2);
1180 	}
1181 	*val_cnt = wrp->wl_rates_num;
1182 
1183 done:
1184 	free(gbuf);
1185 	return (status);
1186 }
1187 
1188 static dladm_status_t
1189 do_get_rate_prop(struct prop_desc *pd, datalink_id_t linkid,
1190     char **prop_val, uint_t *val_cnt, datalink_media_t media)
1191 {
1192 	if (media != DL_WIFI)
1193 		return (dld_speed_get(pd, linkid, prop_val, val_cnt));
1194 
1195 	return (do_get_rate_common(pd, linkid, prop_val, val_cnt,
1196 	    WL_DESIRED_RATES));
1197 }
1198 
1199 static dladm_status_t
1200 do_get_rate_mod(struct prop_desc *pd, datalink_id_t linkid,
1201     char **prop_val, uint_t *val_cnt, datalink_media_t media)
1202 {
1203 	int		i;
1204 
1205 	switch (media) {
1206 	case DL_ETHER:
1207 		if (VALCNT(link_speed_vals) > *val_cnt)
1208 			return (DLADM_STATUS_TOOSMALL);
1209 		for (i = 0; i < VALCNT(link_speed_vals); i++)
1210 			(void) strcpy(prop_val[i], link_speed_vals[i].vd_name);
1211 		*val_cnt = VALCNT(link_speed_vals);
1212 		return (DLADM_STATUS_OK);
1213 
1214 	case DL_WIFI:
1215 		return (do_get_rate_common(pd, linkid, prop_val, val_cnt,
1216 		    WL_SUPPORTED_RATES));
1217 	default:
1218 		return (DLADM_STATUS_BADARG);
1219 	}
1220 }
1221 
1222 static dladm_status_t
1223 do_set_rate(datalink_id_t linkid, dladm_wlan_rates_t *rates)
1224 {
1225 	int		i;
1226 	uint_t		len;
1227 	wldp_t		*gbuf;
1228 	wl_rates_t	*wrp;
1229 	dladm_status_t	status = DLADM_STATUS_OK;
1230 
1231 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
1232 		return (DLADM_STATUS_NOMEM);
1233 
1234 	(void) memset(gbuf, 0, MAX_BUF_LEN);
1235 
1236 	wrp = (wl_rates_t *)gbuf->wldp_buf;
1237 	for (i = 0; i < rates->wr_cnt; i++)
1238 		wrp->wl_rates_rates[i] = rates->wr_rates[i];
1239 	wrp->wl_rates_num = rates->wr_cnt;
1240 
1241 	len = offsetof(wl_rates_t, wl_rates_rates) +
1242 	    (rates->wr_cnt * sizeof (char)) + WIFI_BUF_OFFSET;
1243 	status = i_dladm_wlan_ioctl(linkid, gbuf, WL_DESIRED_RATES, len,
1244 	    WLAN_SET_PARAM, len);
1245 
1246 	free(gbuf);
1247 	return (status);
1248 }
1249 
1250 /* ARGSUSED */
1251 static dladm_status_t
1252 do_set_rate_prop(prop_desc_t *pd, datalink_id_t linkid,
1253     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
1254 {
1255 	dladm_wlan_rates_t	rates;
1256 	dladm_status_t		status;
1257 
1258 	/*
1259 	 * can currently set rate on WIFI links only.
1260 	 */
1261 	if (media != DL_WIFI)
1262 		return (DLADM_STATUS_PROPRDONLY);
1263 
1264 	if (val_cnt != 1)
1265 		return (DLADM_STATUS_BADVALCNT);
1266 
1267 	rates.wr_cnt = 1;
1268 	rates.wr_rates[0] = vdp[0].vd_val;
1269 
1270 	status = do_set_rate(linkid, &rates);
1271 
1272 done:
1273 	return (status);
1274 }
1275 
1276 /* ARGSUSED */
1277 static dladm_status_t
1278 do_check_rate(struct prop_desc *pd, datalink_id_t linkid, char **prop_val,
1279     uint_t val_cnt, val_desc_t *vdp, datalink_media_t media)
1280 {
1281 	int		i;
1282 	uint_t		modval_cnt = MAX_SUPPORT_RATES;
1283 	char		*buf, **modval;
1284 	dladm_status_t	status;
1285 
1286 	if (val_cnt != 1)
1287 		return (DLADM_STATUS_BADVALCNT);
1288 
1289 	buf = malloc((sizeof (char *) + DLADM_STRSIZE) *
1290 	    MAX_SUPPORT_RATES);
1291 	if (buf == NULL) {
1292 		status = DLADM_STATUS_NOMEM;
1293 		goto done;
1294 	}
1295 
1296 	modval = (char **)(void *)buf;
1297 	for (i = 0; i < MAX_SUPPORT_RATES; i++) {
1298 		modval[i] = buf + sizeof (char *) * MAX_SUPPORT_RATES +
1299 		    i * DLADM_STRSIZE;
1300 	}
1301 
1302 	status = do_get_rate_mod(NULL, linkid, modval, &modval_cnt, media);
1303 	if (status != DLADM_STATUS_OK)
1304 		goto done;
1305 
1306 	for (i = 0; i < modval_cnt; i++) {
1307 		if (strcasecmp(*prop_val, modval[i]) == 0) {
1308 			vdp->vd_val = (uintptr_t)(uint_t)
1309 			    (atof(*prop_val) * 2);
1310 			status = DLADM_STATUS_OK;
1311 			break;
1312 		}
1313 	}
1314 	if (i == modval_cnt)
1315 		status = DLADM_STATUS_BADVAL;
1316 done:
1317 	free(buf);
1318 	return (status);
1319 }
1320 
1321 static dladm_status_t
1322 do_get_phyconf(datalink_id_t linkid, wldp_t *gbuf)
1323 {
1324 	return (i_dladm_wlan_get_ioctl(linkid, gbuf, WL_PHY_CONFIG));
1325 }
1326 
1327 /* ARGSUSED */
1328 static dladm_status_t
1329 do_get_channel_prop(struct prop_desc *pd, datalink_id_t linkid,
1330     char **prop_val, uint_t *val_cnt, datalink_media_t media)
1331 {
1332 	uint32_t	channel;
1333 	wldp_t		*gbuf;
1334 	dladm_status_t	status = DLADM_STATUS_OK;
1335 
1336 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
1337 		return (DLADM_STATUS_NOMEM);
1338 
1339 	if ((status = do_get_phyconf(linkid, gbuf)) != DLADM_STATUS_OK)
1340 		goto done;
1341 
1342 	if (!i_dladm_wlan_convert_chan((wl_phy_conf_t *)gbuf->wldp_buf,
1343 	    &channel)) {
1344 		status = DLADM_STATUS_NOTFOUND;
1345 		goto done;
1346 	}
1347 
1348 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%u", channel);
1349 	*val_cnt = 1;
1350 
1351 done:
1352 	free(gbuf);
1353 	return (status);
1354 }
1355 
1356 static dladm_status_t
1357 do_get_powermode(datalink_id_t linkid, wldp_t *gbuf)
1358 {
1359 	return (i_dladm_wlan_get_ioctl(linkid, gbuf, WL_POWER_MODE));
1360 }
1361 
1362 /* ARGSUSED */
1363 static dladm_status_t
1364 do_get_powermode_prop(struct prop_desc *pd, datalink_id_t linkid,
1365     char **prop_val, uint_t *val_cnt, datalink_media_t media)
1366 {
1367 	wl_ps_mode_t	*mode;
1368 	const char	*s;
1369 	wldp_t		*gbuf;
1370 	dladm_status_t	status = DLADM_STATUS_OK;
1371 
1372 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
1373 		return (DLADM_STATUS_NOMEM);
1374 
1375 	if ((status = do_get_powermode(linkid, gbuf)) != DLADM_STATUS_OK)
1376 		goto done;
1377 
1378 	mode = (wl_ps_mode_t *)(gbuf->wldp_buf);
1379 	switch (mode->wl_ps_mode) {
1380 	case WL_PM_AM:
1381 		s = "off";
1382 		break;
1383 	case WL_PM_MPS:
1384 		s = "max";
1385 		break;
1386 	case WL_PM_FAST:
1387 		s = "fast";
1388 		break;
1389 	default:
1390 		status = DLADM_STATUS_NOTFOUND;
1391 		goto done;
1392 	}
1393 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
1394 	*val_cnt = 1;
1395 
1396 done:
1397 	free(gbuf);
1398 	return (status);
1399 }
1400 
1401 static dladm_status_t
1402 do_set_powermode(datalink_id_t linkid, dladm_wlan_powermode_t *pm)
1403 {
1404 	wl_ps_mode_t    ps_mode;
1405 
1406 	(void) memset(&ps_mode, 0xff, sizeof (ps_mode));
1407 
1408 	switch (*pm) {
1409 	case DLADM_WLAN_PM_OFF:
1410 		ps_mode.wl_ps_mode = WL_PM_AM;
1411 		break;
1412 	case DLADM_WLAN_PM_MAX:
1413 		ps_mode.wl_ps_mode = WL_PM_MPS;
1414 		break;
1415 	case DLADM_WLAN_PM_FAST:
1416 		ps_mode.wl_ps_mode = WL_PM_FAST;
1417 		break;
1418 	default:
1419 		return (DLADM_STATUS_NOTSUP);
1420 	}
1421 	return (i_dladm_wlan_set_ioctl(linkid, WL_POWER_MODE, &ps_mode,
1422 	    sizeof (ps_mode)));
1423 }
1424 
1425 /* ARGSUSED */
1426 static dladm_status_t
1427 do_set_powermode_prop(prop_desc_t *pd, datalink_id_t linkid,
1428     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
1429 {
1430 	dladm_wlan_powermode_t powermode = (dladm_wlan_powermode_t)vdp->vd_val;
1431 	dladm_status_t status;
1432 
1433 	if (val_cnt != 1)
1434 		return (DLADM_STATUS_BADVALCNT);
1435 
1436 	status = do_set_powermode(linkid, &powermode);
1437 
1438 	return (status);
1439 }
1440 
1441 static dladm_status_t
1442 do_get_radio(datalink_id_t linkid, wldp_t *gbuf)
1443 {
1444 	return (i_dladm_wlan_get_ioctl(linkid, gbuf, WL_RADIO));
1445 }
1446 
1447 /* ARGSUSED */
1448 static dladm_status_t
1449 do_get_radio_prop(struct prop_desc *pd, datalink_id_t linkid,
1450     char **prop_val, uint_t *val_cnt, datalink_media_t media)
1451 {
1452 	wl_radio_t	radio;
1453 	const char	*s;
1454 	wldp_t		*gbuf;
1455 	dladm_status_t	status = DLADM_STATUS_OK;
1456 
1457 	if ((gbuf = malloc(MAX_BUF_LEN)) == NULL)
1458 		return (DLADM_STATUS_NOMEM);
1459 
1460 	if ((status = do_get_radio(linkid, gbuf)) != DLADM_STATUS_OK)
1461 		goto done;
1462 
1463 	radio = *(wl_radio_t *)(gbuf->wldp_buf);
1464 	switch (radio) {
1465 	case B_TRUE:
1466 		s = "on";
1467 		break;
1468 	case B_FALSE:
1469 		s = "off";
1470 		break;
1471 	default:
1472 		status = DLADM_STATUS_NOTFOUND;
1473 		goto done;
1474 	}
1475 	(void) snprintf(*prop_val, DLADM_STRSIZE, "%s", s);
1476 	*val_cnt = 1;
1477 
1478 done:
1479 	free(gbuf);
1480 	return (status);
1481 }
1482 
1483 static dladm_status_t
1484 do_set_radio(datalink_id_t linkid, dladm_wlan_radio_t *radio)
1485 {
1486 	wl_radio_t r;
1487 
1488 	switch (*radio) {
1489 	case DLADM_WLAN_RADIO_ON:
1490 		r = B_TRUE;
1491 		break;
1492 	case DLADM_WLAN_RADIO_OFF:
1493 		r = B_FALSE;
1494 		break;
1495 	default:
1496 		return (DLADM_STATUS_NOTSUP);
1497 	}
1498 	return (i_dladm_wlan_set_ioctl(linkid, WL_RADIO, &r, sizeof (r)));
1499 }
1500 
1501 /* ARGSUSED */
1502 static dladm_status_t
1503 do_set_radio_prop(prop_desc_t *pd, datalink_id_t linkid,
1504     val_desc_t *vdp, uint_t val_cnt, uint_t fags, datalink_media_t media)
1505 {
1506 	dladm_wlan_radio_t radio = (dladm_wlan_radio_t)vdp->vd_val;
1507 	dladm_status_t status;
1508 
1509 	if (val_cnt != 1)
1510 		return (DLADM_STATUS_BADVALCNT);
1511 
1512 	status = do_set_radio(linkid, &radio);
1513 
1514 	return (status);
1515 }
1516 
1517 static dladm_status_t
1518 i_dladm_set_linkprop_db(datalink_id_t linkid, const char *prop_name,
1519     char **prop_val, uint_t val_cnt)
1520 {
1521 	char		buf[MAXLINELEN];
1522 	int		i;
1523 	dladm_conf_t	conf;
1524 	dladm_status_t	status;
1525 
1526 	status = dladm_read_conf(linkid, &conf);
1527 	if (status != DLADM_STATUS_OK)
1528 		return (status);
1529 
1530 	/*
1531 	 * reset case.
1532 	 */
1533 	if (val_cnt == 0) {
1534 		status = dladm_unset_conf_field(conf, prop_name);
1535 		if (status == DLADM_STATUS_OK)
1536 			status = dladm_write_conf(conf);
1537 		goto done;
1538 	}
1539 
1540 	buf[0] = '\0';
1541 	for (i = 0; i < val_cnt; i++) {
1542 		(void) strlcat(buf, prop_val[i], MAXLINELEN);
1543 		if (i != val_cnt - 1)
1544 			(void) strlcat(buf, ",", MAXLINELEN);
1545 	}
1546 
1547 	status = dladm_set_conf_field(conf, prop_name, DLADM_TYPE_STR, buf);
1548 	if (status == DLADM_STATUS_OK)
1549 		status = dladm_write_conf(conf);
1550 
1551 done:
1552 	dladm_destroy_conf(conf);
1553 	return (status);
1554 }
1555 
1556 static dladm_status_t
1557 i_dladm_get_linkprop_db(datalink_id_t linkid, const char *prop_name,
1558     char **prop_val, uint_t *val_cntp)
1559 {
1560 	char		buf[MAXLINELEN], *str;
1561 	uint_t		cnt = 0;
1562 	dladm_conf_t	conf;
1563 	dladm_status_t	status;
1564 
1565 	status = dladm_read_conf(linkid, &conf);
1566 	if (status != DLADM_STATUS_OK)
1567 		return (status);
1568 
1569 	status = dladm_get_conf_field(conf, prop_name, buf, MAXLINELEN);
1570 	if (status != DLADM_STATUS_OK)
1571 		goto done;
1572 
1573 	str = strtok(buf, ",");
1574 	while (str != NULL) {
1575 		if (cnt == *val_cntp) {
1576 			status = DLADM_STATUS_TOOSMALL;
1577 			goto done;
1578 		}
1579 		(void) strlcpy(prop_val[cnt++], str, DLADM_PROP_VAL_MAX);
1580 		str = strtok(NULL, ",");
1581 	}
1582 
1583 	*val_cntp = cnt;
1584 
1585 done:
1586 	dladm_destroy_conf(conf);
1587 	return (status);
1588 }
1589 
1590 static dld_public_prop_t *
1591 dladm_name2prop(const char *prop_name)
1592 {
1593 	dld_public_prop_t *p;
1594 
1595 	for (p = dld_prop; p->pp_id != DLD_PROP_PRIVATE; p++) {
1596 		if (strcmp(p->pp_name, prop_name) == 0)
1597 			break;
1598 	}
1599 	return (p);
1600 }
1601 
1602 
1603 static dld_ioc_prop_t *
1604 dld_buf_alloc(size_t valsize, datalink_id_t linkid, const char *prop_name,
1605     dladm_status_t *status)
1606 {
1607 	int dsize;
1608 	dld_ioc_prop_t *dip;
1609 	dld_public_prop_t *p;
1610 
1611 	*status = DLADM_STATUS_OK;
1612 	p = dladm_name2prop(prop_name);
1613 	if (p->pp_id != DLD_PROP_PRIVATE)
1614 		valsize = p->pp_valsize;
1615 
1616 	dsize = DLD_PROPBUF_SIZE(valsize);
1617 	dip = malloc(dsize);
1618 	if (dip == NULL) {
1619 		*status = DLADM_STATUS_NOMEM;
1620 		return (NULL);
1621 	}
1622 	bzero(dip, dsize);
1623 	dip->pr_valsize = valsize;
1624 	(void) strlcpy(dip->pr_name, prop_name, DLD_LINKPROP_NAME_MAX);
1625 	dip->pr_version = DLD_PROP_VERSION;
1626 	dip->pr_linkid = linkid;
1627 	dip->pr_num = p->pp_id;
1628 	return (dip);
1629 }
1630 
1631 /* ARGSUSED */
1632 static dladm_status_t
1633 dld_set_public_prop(prop_desc_t *pd, datalink_id_t linkid,
1634     val_desc_t *vdp, uint_t val_cnt, uint_t flags, datalink_media_t media)
1635 {
1636 	dld_ioc_prop_t	*dip;
1637 	int		fd, dsize;
1638 	dladm_status_t	status = DLADM_STATUS_OK;
1639 	uint8_t		u8;
1640 	uint16_t	u16;
1641 	uint32_t	u32;
1642 	void		*val;
1643 
1644 	dip = dld_buf_alloc(0, linkid, pd->pd_name, &status);
1645 	if (dip == NULL)
1646 		return (status);
1647 
1648 	if (pd->pd_flags & PD_CHECK_ALLOC)
1649 		val = (void *)vdp->vd_val;
1650 	else {
1651 		/*
1652 		 * Currently all 1/2/4-byte size properties are byte/word/int.
1653 		 * No need (yet) to distinguish these from arrays of same size.
1654 		 */
1655 		switch (dip->pr_valsize) {
1656 		case 1:
1657 			u8 = vdp->vd_val;
1658 			val = &u8;
1659 			break;
1660 		case 2:
1661 			u16 = vdp->vd_val;
1662 			val = &u16;
1663 			break;
1664 		case 4:
1665 			u32 = vdp->vd_val;
1666 			val = &u32;
1667 			break;
1668 		default:
1669 			val = &vdp->vd_val;
1670 			break;
1671 		}
1672 	}
1673 
1674 	(void) memcpy(dip->pr_val, val, dip->pr_valsize);
1675 	dsize = DLD_PROPBUF_SIZE(dip->pr_valsize);
1676 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) {
1677 		status = dladm_errno2status(errno);
1678 		goto done;
1679 	}
1680 	if (i_dladm_ioctl(fd, DLDIOCSETPROP, dip, dsize) < 0)
1681 		status = dladm_errno2status(errno);
1682 
1683 	(void) close(fd);
1684 done:
1685 	return (status);
1686 }
1687 
1688 static dladm_status_t
1689 dld_get_public_prop(dld_ioc_prop_t *dip)
1690 {
1691 	int fd, dsize;
1692 	dladm_status_t status = DLADM_STATUS_OK;
1693 
1694 	dsize = DLD_PROPBUF_SIZE(dip->pr_valsize);
1695 
1696 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) {
1697 		status = dladm_errno2status(errno);
1698 		goto done;
1699 	}
1700 	if (i_dladm_ioctl(fd, DLDIOCGETPROP, dip, dsize) < 0) {
1701 		status = dladm_errno2status(errno);
1702 	}
1703 done:
1704 	return (status);
1705 }
1706 
1707 /* ARGSUSED */
1708 static dladm_status_t
1709 dld_defmtu_check(struct prop_desc *pd, datalink_id_t linkid, char **prop_val,
1710     uint_t val_cnt, val_desc_t *v, datalink_media_t media)
1711 {
1712 	uint64_t mtu;
1713 
1714 	if (val_cnt != 1)
1715 		return (DLADM_STATUS_BADVAL);
1716 	mtu = atoll(prop_val[0]);
1717 	v->vd_val = (uintptr_t)malloc(sizeof (uint64_t));
1718 	if ((void *)v->vd_val == NULL)
1719 		return (DLADM_STATUS_NOMEM);
1720 	bcopy(&mtu, (void *)v->vd_val, sizeof (mtu));
1721 	return (DLADM_STATUS_OK);
1722 }
1723 
1724 /* ARGSUSED */
1725 static dladm_status_t
1726 dld_duplex_get(struct prop_desc *pd, datalink_id_t linkid,
1727     char **prop_val, uint_t *val_cnt, datalink_media_t media)
1728 {
1729 	link_duplex_t   link_duplex;
1730 	dladm_status_t  status;
1731 
1732 	if ((status = dladm_get_single_mac_stat(linkid, "link_duplex",
1733 	    KSTAT_DATA_UINT32, &link_duplex)) != 0)
1734 		return (status);
1735 
1736 	switch (link_duplex) {
1737 	case LINK_DUPLEX_FULL:
1738 		(void) strcpy(*prop_val, "full");
1739 		break;
1740 	case LINK_DUPLEX_HALF:
1741 		(void) strcpy(*prop_val, "half");
1742 		break;
1743 	default:
1744 		(void) strcpy(*prop_val, "unknown");
1745 		break;
1746 	}
1747 	*val_cnt = 1;
1748 	return (DLADM_STATUS_OK);
1749 }
1750 
1751 /* ARGSUSED */
1752 static dladm_status_t
1753 dld_speed_get(struct prop_desc *pd, datalink_id_t linkid,
1754     char **prop_val, uint_t *val_cnt)
1755 {
1756 	uint64_t	ifspeed = 0;
1757 	dladm_status_t status;
1758 
1759 	if ((status = dladm_get_single_mac_stat(linkid, "ifspeed",
1760 	    KSTAT_DATA_UINT64, &ifspeed)) != 0)
1761 		return (status);
1762 	if ((ifspeed % 1000000) != 0) {
1763 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX,
1764 		    "%llf", ifspeed / (float)1000000); /* Mbps */
1765 	} else {
1766 		(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX,
1767 		    "%llu", ifspeed / 1000000); /* Mbps */
1768 	}
1769 	*val_cnt = 1;
1770 	return (DLADM_STATUS_OK);
1771 }
1772 
1773 /* ARGSUSED */
1774 static dladm_status_t
1775 dld_status_get(struct prop_desc *pd, datalink_id_t linkid,
1776     char **prop_val, uint_t *val_cnt, datalink_media_t media)
1777 {
1778 	link_state_t   link_state;
1779 	dladm_status_t status;
1780 
1781 	if ((status = dladm_get_single_mac_stat(linkid, "link_state",
1782 	    KSTAT_DATA_UINT32, &link_state)) != 0)
1783 		return (status);
1784 
1785 	switch (link_state) {
1786 	case LINK_STATE_UP:
1787 		(void) strcpy(*prop_val, "up");
1788 		break;
1789 	case LINK_STATE_DOWN:
1790 		(void) strcpy(*prop_val, "down");
1791 		break;
1792 	default:
1793 		(void) strcpy(*prop_val, "unknown");
1794 		break;
1795 	}
1796 	*val_cnt = 1;
1797 	return (DLADM_STATUS_OK);
1798 }
1799 
1800 /* ARGSUSED */
1801 static dladm_status_t
1802 dld_binary_get(struct prop_desc *pd, datalink_id_t linkid,
1803     char **prop_val, uint_t *val_cnt, datalink_media_t media)
1804 {
1805 	dld_ioc_prop_t *dip;
1806 	dladm_status_t status;
1807 
1808 	if ((dip = dld_buf_alloc(0, linkid, pd->pd_name, &status)) == NULL)
1809 		return (status);
1810 	if ((status = dld_get_public_prop(dip)) != DLADM_STATUS_OK) {
1811 		free(dip);
1812 		return (status);
1813 	}
1814 	(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%x", dip->pr_val[0]);
1815 	free(dip);
1816 	*val_cnt = 1;
1817 	return (DLADM_STATUS_OK);
1818 }
1819 
1820 /* ARGSUSED */
1821 static dladm_status_t
1822 dld_uint64_get(struct prop_desc *pd, datalink_id_t linkid,
1823     char **prop_val, uint_t *val_cnt, datalink_media_t media)
1824 {
1825 	dld_ioc_prop_t *dip;
1826 	uint64_t v  = 0;
1827 	uchar_t *cp;
1828 	dladm_status_t status;
1829 
1830 	if ((dip = dld_buf_alloc(0, linkid, pd->pd_name, &status)) == NULL)
1831 		return (status);
1832 	if ((status = dld_get_public_prop(dip)) != DLADM_STATUS_OK) {
1833 		free(dip);
1834 		return (status);
1835 	}
1836 	cp = (uchar_t *)dip->pr_val;
1837 	(void) memcpy(&v, cp, sizeof (v));
1838 	(void) snprintf(*prop_val, DLADM_PROP_VAL_MAX, "%" PRIu64, v);
1839 	free(dip);
1840 	*val_cnt = 1;
1841 	return (DLADM_STATUS_OK);
1842 }
1843 
1844 /* ARGSUSED */
1845 static dladm_status_t
1846 dld_flowctl_get(struct prop_desc *pd, datalink_id_t linkid,
1847     char **prop_val, uint_t *val_cnt, datalink_media_t media)
1848 {
1849 	dld_ioc_prop_t *dip;
1850 	link_flowctrl_t v;
1851 	dladm_status_t status;
1852 	uchar_t *cp;
1853 
1854 	if ((dip = dld_buf_alloc(0, linkid, pd->pd_name, &status)) == NULL)
1855 		return (status);
1856 
1857 	if ((status = dld_get_public_prop(dip)) != DLADM_STATUS_OK) {
1858 		free(dip);
1859 		return (status);
1860 	}
1861 	cp = (uchar_t *)dip->pr_val;
1862 	(void) memcpy(&v, cp, sizeof (v));
1863 	switch (v) {
1864 	case LINK_FLOWCTRL_NONE:
1865 		(void) sprintf(*prop_val, "no");
1866 		break;
1867 	case LINK_FLOWCTRL_RX:
1868 		(void) sprintf(*prop_val, "rx");
1869 		break;
1870 	case LINK_FLOWCTRL_TX:
1871 		(void) sprintf(*prop_val, "tx");
1872 		break;
1873 	case LINK_FLOWCTRL_BI:
1874 		(void) sprintf(*prop_val, "bi");
1875 		break;
1876 	}
1877 	free(dip);
1878 	*val_cnt = 1;
1879 	return (DLADM_STATUS_OK);
1880 }
1881 
1882 
1883 /* ARGSUSED */
1884 static dladm_status_t
1885 dld_set_prop(datalink_id_t linkid, const char *prop_name,
1886     char **prop_val, uint_t val_cnt, uint_t flags)
1887 {
1888 	int		fd, i, slen;
1889 	int 		bufsize = 0, dsize;
1890 	dld_ioc_prop_t *dip = NULL;
1891 	uchar_t 	*dp;
1892 	dld_public_prop_t *p;
1893 	dladm_status_t	status;
1894 
1895 	if ((prop_name == NULL && prop_val != NULL) ||
1896 	    (prop_val != NULL && val_cnt == 0))
1897 		return (DLADM_STATUS_BADARG);
1898 	p = dladm_name2prop(prop_name);
1899 	if (p->pp_id != DLD_PROP_PRIVATE)
1900 		return (DLADM_STATUS_BADARG);
1901 
1902 	/*
1903 	 * private properties: all parsing is done in the kernel.
1904 	 * allocate a enough space for each property + its separator (',').
1905 	 */
1906 	for (i = 0; i < val_cnt; i++) {
1907 		bufsize += strlen(prop_val[i]) + 1;
1908 	}
1909 	dip = dld_buf_alloc(bufsize + 1, linkid, prop_name, &status);
1910 	if (dip == NULL)
1911 		return (status);
1912 
1913 	dp = (uchar_t *)dip->pr_val;
1914 	dsize = sizeof (dld_ioc_prop_t) + bufsize;
1915 	slen = 0;
1916 	for (i = 0; i < val_cnt; i++) {
1917 		int plen = 0;
1918 
1919 		plen = strlen(prop_val[i]);
1920 		bcopy(prop_val[i], dp, plen);
1921 		slen += plen;
1922 		/*
1923 		 * add a "," separator and update dp.
1924 		 */
1925 		if (i != (val_cnt -1))
1926 			dp[slen++] = ',';
1927 		dp += (plen + 1);
1928 	}
1929 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0) {
1930 		free(dip);
1931 		return (dladm_errno2status(errno));
1932 	}
1933 	if ((status = i_dladm_ioctl(fd, DLDIOCSETPROP, dip, dsize)) < 0) {
1934 		free(dip);
1935 		return (status);
1936 	}
1937 	free(dip);
1938 	(void) close(fd);
1939 	return (DLADM_STATUS_OK);
1940 }
1941 
1942 static dladm_status_t
1943 dld_get_prop(datalink_id_t linkid, const char *prop_name,
1944     char **prop_val, uint_t *val_cnt, dladm_prop_type_t type)
1945 {
1946 	int		fd;
1947 	dladm_status_t  status = DLADM_STATUS_OK;
1948 	uint_t 		dsize;
1949 	dld_ioc_prop_t *dip = NULL;
1950 	dld_public_prop_t *p;
1951 	char tmp = '\0';
1952 
1953 	if ((prop_name == NULL && prop_val != NULL) ||
1954 	    (prop_val != NULL && val_cnt == 0))
1955 		return (DLADM_STATUS_BADARG);
1956 
1957 	p = dladm_name2prop(prop_name);
1958 	if (p->pp_id != DLD_PROP_PRIVATE)
1959 		return (DLADM_STATUS_BADARG);
1960 
1961 	if (type == DLADM_PROP_VAL_DEFAULT ||
1962 	    type == DLADM_PROP_VAL_MODIFIABLE) {
1963 		*prop_val = &tmp;
1964 		*val_cnt = 1;
1965 		return (DLADM_STATUS_OK);
1966 	}
1967 
1968 	/*
1969 	 * private properties: all parsing is done in the kernel.
1970 	 */
1971 	dip = dld_buf_alloc(1024, linkid, prop_name, &status);
1972 	if (dip == NULL)
1973 		return (status);
1974 	dsize = DLD_PROPBUF_SIZE(dip->pr_valsize);
1975 
1976 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
1977 		return (DLADM_STATUS_BADARG);
1978 
1979 	if ((status = i_dladm_ioctl(fd, DLDIOCGETPROP, dip, dsize)) < 0) {
1980 		status = dladm_errno2status(errno);
1981 	} else {
1982 		(void) strncpy(*prop_val, dip->pr_val, DLADM_PROP_VAL_MAX);
1983 		*val_cnt = 1;
1984 	}
1985 	return (status);
1986 }
1987