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